Visual Studio references the ServiceStack SOAP method

I have a simple API setup using ServiceStack. To run it, I use the following code:

namespace TheGuest.Test { [DataContract] [Description("A sample web service.")] public class Greet { [DataMember] public string Name { get; set; } } [DataContract] public class GreetResponse { [DataMember] public string Result { get; set; } } /// <summary> /// An example of a very basic web service. /// </summary> public class GreetService : IService<Greet> { public object Execute(Greet request) { return new GreetResponse { Result = "Hello " + request.Name }; } } public static class Constants { public const string DefaultNamespaceV1 = "http://my/custom/namespace"; } public class MyAppHost : AppHostBase { // Tell Service Stack the name of your application and where to find your web services. public MyAppHost() : base("My Web Services", typeof(GreetService).Assembly) { } public override void Configure(Container container) { SetConfig(new EndpointHostConfig { WsdlServiceNamespace = Constants.DefaultNamespaceV1 }); // Register user-defined REST-ful URLs. Routes .Add<Greet>("/hello") .Add<Greet>("/hello/{Name}") .Add<Greet>("/hello/{Name*}"); } } public class MvcApplication : HttpApplication { protected void Application_Start() { new MyAppHost().Init(); } } } 

And adding the following line to AssemblyInfo.cs:

 [assembly: ContractNamespace("http://my/custom/namespace", ClrNamespace = "TheGuest.Test")] 

It will generate the following WSDL:

 <?xml version="1.0" encoding="utf-8"?> <wsdl:definitions name="Soap12" targetNamespace="http://my/custom/namespace" xmlns:svc="http://my/custom/namespace" xmlns:tns="http://my/custom/namespace" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex"> <wsdl:types> <xs:schema xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="anyType" nillable="true" type="xs:anyType" /> <xs:element name="anyURI" nillable="true" type="xs:anyURI" /> <xs:element name="base64Binary" nillable="true" type="xs:base64Binary" /> <xs:element name="boolean" nillable="true" type="xs:boolean" /> <xs:element name="byte" nillable="true" type="xs:byte" /> <xs:element name="dateTime" nillable="true" type="xs:dateTime" /> <xs:element name="decimal" nillable="true" type="xs:decimal" /> <xs:element name="double" nillable="true" type="xs:double" /> <xs:element name="float" nillable="true" type="xs:float" /> <xs:element name="int" nillable="true" type="xs:int" /> <xs:element name="long" nillable="true" type="xs:long" /> <xs:element name="QName" nillable="true" type="xs:QName" /> <xs:element name="short" nillable="true" type="xs:short" /> <xs:element name="string" nillable="true" type="xs:string" /> <xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte" /> <xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt" /> <xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong" /> <xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort" /> <xs:element name="char" nillable="true" type="tns:char" /> <xs:simpleType name="char"> <xs:restriction base="xs:int" /> </xs:simpleType> <xs:element name="duration" nillable="true" type="tns:duration" /> <xs:simpleType name="duration"> <xs:restriction base="xs:duration"> <xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?" /> <xs:minInclusive value="-P10675199DT2H48M5.4775808S" /> <xs:maxInclusive value="P10675199DT2H48M5.4775807S" /> </xs:restriction> </xs:simpleType> <xs:element name="guid" nillable="true" type="tns:guid" /> <xs:simpleType name="guid"> <xs:restriction base="xs:string"> <xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}" /> </xs:restriction> </xs:simpleType> <xs:attribute name="FactoryType" type="xs:QName" /> <xs:attribute name="Id" type="xs:ID" /> <xs:attribute name="Ref" type="xs:IDREF" /> </xs:schema> <xs:schema xmlns:tns="http://my/custom/namespace" elementFormDefault="qualified" targetNamespace="http://my/custom/namespace" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="Greet"> <xs:sequence> <xs:element minOccurs="0" name="Name" nillable="true" type="xs:string" /> </xs:sequence> </xs:complexType> <xs:element name="Greet" nillable="true" type="tns:Greet" /> </xs:schema> </wsdl:types> <wsdl:message name="GreetIn"> <wsdl:part name="parameters" element="tns:Greet" /> </wsdl:message> <wsdl:portType name="ISyncReply"> </wsdl:portType> <wsdl:portType name="IOneWay"> <wsdl:operation name="Greet"> <wsdl:input message="svc:GreetIn" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="WSHttpBinding_ISyncReply" type="svc:ISyncReply"> <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" /> </wsdl:binding> <wsdl:binding name="WSHttpBinding_IOneWay" type="svc:IOneWay"> <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="Greet"> <soap:operation soapAction="http://schemas.servicestack.net/types/Greet" style="document" /> <wsdl:input> <soap:body use="literal" /> </wsdl:input> </wsdl:operation> </wsdl:binding> <wsdl:service name="SyncReply"> <wsdl:port name="WSHttpBinding_ISyncReply" binding="svc:WSHttpBinding_ISyncReply"> <soap:address location="http://localhost:50472/test/soap12" /> </wsdl:port> </wsdl:service> <wsdl:service name="AsyncOneWay"> <wsdl:port name="WSHttpBinding_IOneWay" binding="svc:WSHttpBinding_IOneWay"> <soap:address location="http://localhost:50472/test/soap12" /> </wsdl:port> </wsdl:service> </wsdl:definitions> 

When I add this service through Visual Studio 2010, I get 2 clients. One of them is called "SyncReplyClient", which has no methods that I can call, and the other is called "OneWayClient" using the "Welcome" method. But as the names imply, I would like to use SyncReplyClient, since I need an answer.

How do I achieve this?

On the one hand, OneWayClient throws a ProtocolException with the following message: "One-way operation returns a non-zero message with Action = ''." which does not bother me since I do not want to use OneWayClient, but it is strange nonetheless.

+4
source share
3 answers

Make sure you read about the limitations of SOAP when creating the services that you want to use SOAP. those. you need to save one XSD / WSDL namespace. For instance. You can change the default WSDL namespace in your AppConfig with:

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

Specifies which WSDL / XSD namespace will be used on the generated WSDL page. You also need to map this XSD namespace to your [DataContract] DTO, specifying a namespace for each DataContract, which you can do by manually specifying each

 [DataContract(Namespace="http://my.new.namespace.com/types")] 

or you can specify

  [assembly: ContractNamespace("http://my/custom/namespace", ClrNamespace = "TheGuest.Test")] 

to install it on multiple DTOs in a common C # namespace.

Also, several things have changed lately, we added a New API and added different attributes so that you can annotate your services (which will appear in the metadata / and Api Docs / Swagger pages). Given these changes, a new way to create your service:

 [DataContract] [Api("A sample web service.")] public class Greet { [DataMember] [ApiMember("The name of the person you wish to greet")] public string Name { get; set; } } [DataContract] public class GreetResponse { [DataMember] public string Result { get; set; } } public class GreetService : Service { public GreetResponse Any(Greet request) { return new GreetResponse { Result = "Hello " + request.Name }; } } 

Tell ServiceStack what type of service response

In order for ServiceStack to determine the response type of your service, you must specify any of the following tips:

Using Type Return Type

Your services can either return type object , or now ResponseDto , for example:

 public class GreetService : Service { //1. Using Object public object Any(Greet request) { return new GreetResponse { Result = "Hello " + request.Name }; } //2. Above service with a strong response type public GreetResponse Any(Greet request) { return new GreetResponse { Result = "Hello " + request.Name }; } } 

If you use option 2) ServiceStack will use the GreetResponse type.

Use the IReturn Marker Interface

 [DataContract] public class Greet : IReturn<GreetResponse> { ... } 

Another advantage of using the Marker interface is that it provides a more concise client API, for example:

 GreetResponse response = client.Send(new Greet { Name = "World!" }); 

If you did not have a Marker interface, the client API would be:

 GreetResponse response = client.Send<GreetResponse>(new Greet { Name = "World!" }); 

Use typeof naming convention (RequestDto) .Name + 'Response'

If your services have an object response type and a token interface, than you can use the {RequestDto}Response naming convention to tell ServiceStack what the response type is.

Note. For ServiceStack to find the type of response, it must be in the same namespace as the DTO request. In addition, each DTO request and response must be uniquely named, this is what allows you to call the ServiceStack service only with the name of the DTO request , and not with the full namespace.

+5
source

Is there a reason why you refer to the service and generate a proxy class? (Sorry to ask for clarification, but I don’t have enough prestige to post a comment on this.)

If this is your first ServiceStack service, then you should read about how this is a different paradigm than WCF. Here is a brief description.

The great thing about SS is that you don’t need to create proxies, and you can focus on transferring data instead of performing remote actions. It’s just that you have a DLL that points to the DTO, and that DLL refers to both the service project and the client projects.

This allows you to use the built-in clients of the service (JSON / SOAP / etc) to communicate with the service. It will take a little time to figure out the differences, but as soon as you find out how ServiceStack is different (especially regarding calling a service from Javascript or other clients other than C #), and you need to reorganize your service level without restoring proxies, then you will see why it is better.

Sorry if you have a special need for creating a client proxy, and this does not answer the question; this link shows how easy it is to get through shared clients, and I highly recommend using this approach if there is no specific reason for the SOAP generation.

+2
source

I found him!

I checked the ServiceStack examples and they work, but my program does not work.

The difference is that the operation (SayHello) and the response (SayHelloResponse) were not in the same namespace (My.Application.Operations and MyApplication.Responses).

When I put them in the same namespace, it works, my service is no longer "OneWay", but "SyncReply".

+1
source

Source: https://habr.com/ru/post/1440329/


All Articles