I am using Ninject.Extensions.Wcf in a WCF service, and also have the Ninject.Web.Common package in my project.
In the NinjectWebCommon class this is how I bind my dependency
kernel.Bind<ISomething>().To<ConcreteSomething>();
straightforward binding, and everything works..
Now, I want to do the resolution based on some members in the message request body, so I create a provider
kernel.Bind<ISomething>().ToProvider<SomethingProvider>();
And SomethingProvider tries to read the request message like this
public class SomethingProvider : Provider<ISomething>
{
protected override ISomething CreateInstance(IContext context)
{
var message = OperationContext.Current.RequestContext.RequestMessage;
//message.State is Read here, so CreateBufferedCopy throws an InvalidOperationException.
var buffer = message.CreateBufferedCopy(int.MaxValue);
message = buffer.CreateMessage();
var messageCopy = buffer.CreateMessage();
var body = messageCopy.GetBody<string>();
//Parse body, check some members and return one of the "Somethings"
return new AnotherConcreteSomething();
}
}
This throws an invalid operation exception. The RequestMessage in the operationcontext is already in read state, so I cant read it again.
I read that RequestMessage.ToString() is not always reliable since it truncates the body. And, I will have to get the body from the whole SOAP message.
Is there any other way I can do this? Is this approach even correct?
Related
I have a client using HttpClient.GetAsync to call into a Azure Function Http Trigger in .Net 5.
When I call the function using PostMan, I get my custom header data.
However, when I try to access my response object (HttpResponseMessage) that is returned from HttpClient.GetAsync, my header data empty.
I have my Content data and my Status Code. But my custom header data are missing.
Any insight would be appreciated since I have looking at this for hours.
Thanks for you help.
Edit: Here is the code where I am making the http call:
public async Task<HttpResponseMessage> GetQuotesAsync(int? pageNo, int? pageSize, string searchText)
{
var requestUri = $"{RequestUri.Quotes}?pageNo={pageNo}&pageSize={pageSize}&searchText={searchText}";
return await _httpClient.GetAsync(requestUri);
}
Edit 8/8/2021: See my comment below. The issue has something to do with using Blazor Wasm Client.
For anyone having problems after following the tips on this page, go back and check the configuration in the host.json file. you need the Access-Control-Expose-Headers set to * or they won't be send even if you add them. Note: I added the "extensions" node below and removed my logging settings for clarity.
host.json (sample file):
{
"version": "2.0",
"extensions": {
"http": {
"customHeaders": {
"Access-Control-Expose-Headers": "*"
}
}
}
}
This is because HttpResponseMessage's Headers property data type is HttpResponseHeaders but HttpResponseData's Headers property data type is HttpHeadersCollection. Since, they are different, HttpResponseHeaders could not bind to HttpHeadersCollection while calling HttpClient.GetAsync(as it returns HttpResponseMessage).
I could not find a way to read HttpHeadersCollection through HttpClient.
As long as your Azure function code is emitting the header value, you should be able to read that in your client code from the Headers collection of HttpResponseMessage. Nothing in your azure function (which is your remote endpoint you are calling) makes it any different. Remember, your client code has no idea how your remote endpoint is implemented. Today it is azure functions, tomorrow it may be a full blown aspnet core web api or a REST endpoint written in Node.js. Your client code does not care. All it cares is whether the Http response it received has your expected header.
Asumming you have an azure function like this where you are adding a header called total-count to the response.
[Function("quotes")]
public static async Task<HttpResponseData> RunAsync(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("Quotes");
logger.LogInformation("C# HTTP trigger function processed a request for Quotes.");
var quotes = new List<Quote>
{
new Quote { Text = "Hello", ViewCount = 100},
new Quote { Text = "Azure Functions", ViewCount = 200}
};
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("total-count", quotes.Count.ToString());
await response.WriteAsJsonAsync(quotes);
return response;
}
Your existing client code should work as long as you read the Headers property.
public static async Task<HttpResponseMessage> GetQuotesAsync()
{
var requestUri = "https://shkr-playground.azurewebsites.net/api/quotes";
return await _httpClient.GetAsync(requestUri);
}
Now your GetQuotesAsync method can be called somewhere else where you will use the return value of it (HttpResponseMessage instance) and read the headers. In the below example, I am reading that value and adding to a string variable. HttpResponseMessage implements IDisposable. So I am using a using construct to implicitly call the Dispose method.
var msg = "Total count from response headers:";
using (var httpResponseMsg = await GetQuotesAsync())
{
if (httpResponseMsg.Headers.TryGetValues("total-count", out var values))
{
msg += values.FirstOrDefault();
}
}
// TODO: use "msg" variable as needed.
The types which Azure function uses for dealing with response headers is more of an implementation concern of azure functions. It has no impact on your client code where you are using HttpClient and HttpResponseMessage. Your client code is simply dealing with standard http call response (response headers and body)
The issue is not with Blazor WASM, rather if that header has been exposed on your API Side. In your azure function, add the following -
Note: Postman will still show the headers even if you don't expose the headers like below. That's because, Postman doesn't care about CORS headers. CORS is just a browser concept and not a strong security mechanism. It allows you to restrict which other web apps may use your backend resources and that's all.
First create a Startup File to inject the HttpContextAccessor
Package Required: Microsoft.Azure.Functions.Extensions
[assembly: FunctionsStartup(typeof(FuncAppName.Startup))]
namespace FuncAppName
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddScoped<HttpContextAccessor>();
}
}
}
Next, inject it into your main Function -
using Microsoft.AspNetCore.Http;
namespace FuncAppName
{
public class SomeFunction
{
private readonly HttpContext _httpContext;
public SomeFunction(HttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
[FunctionName("SomeFunc")]
public override Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, new[] { "post" }, Route = "run")] HttpRequest req)
{
var response = "Some Response"
_httpContext.Response.Headers.Add("my-custom-header", "some-custom-value");
_httpContext.Response.Headers.Add("my-other-header", "some-other-value");
_httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "my-custom-header, my-other-header");
return new OkObjectResult(response)
}
If you want to allow all headers you can use wildcard (I think, not tested) -
_httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "*");
You still need to add your web-app url to the azure platform CORS. You can add * wildcard, more info here - https://iotespresso.com/allowing-all-cross-origin-requests-azure-functions/
to enable CORS for Local Apps during development - https://stackoverflow.com/a/60109518/9276081
Now to access those headers in your Blazor WASM, as an e.g. you can -
protected override async Task OnInitializedAsync()
{
var content = JsonContent.Create(new { query = "" });
using (var client = new HttpClient())
{
var result = await client.PostAsync("https://func-app-name.azurewebsites.net/api/run", content);
var headers = result.Headers.ToList();
}
}
I have a very basic WCF service which has a method named SaveSchoolName(string SchoolName) which basically returns as boolean value of True if the operation is good.
.
I added a service reference to my client application and is consuming the service as follows:
MyService.WebServicesClient svc = new MyService.WebServicesClient();
bool dataSaved = false;
dataSaved = svc.SaveSchoolName("School Name");
if(dataSaved){
// do something.
}
else{
// log not saved.
}
I want to know how do I determine the Http Status Code (200 - OK) for the WCF Service call. I have tried to search but none seems to provide any detailed info on how I would be able to get the response headers from invoking the method.
You need to create a client message inspector for this.
Check the below code out ... to make it work just add the inspector to your client. BTW, obviously this only works for HTTP :)
public class HttpStatusCodeMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name))
{
var httpResponseProperty = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
Console.WriteLine($"Response status is {(int)httpResponseProperty.StatusCode}");
}
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
To extend #Jorge's answer, I started by implementing the IClientMessageInspector and IEndpointBehavior from the example here. I replaced the contents of IClientMessageInspector.AfterReceiveReply with the code provided by Jorge:
public void AfterReceiveReply(ref Message reply, object correlationState)
{
if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name))
{
var httpResponseProperty = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
Console.WriteLine($"Response status is {(int)httpResponseProperty.StatusCode}");
}
}
Now, you can choose to continue inheriting BehaviorExtensionElement and add the behavior to your client configuration file. If you take this approach you'll be done.
However, since my client configuration was created programmatically, I needed a few additional steps here.
In the docs you can see that an Endpoint Behavior can be added by using the ServiceEndpoint.Behaviors.Add method. But how do we get access to the ServiceEndpoint object?
This can be done by first creating your service client object, and then using its Endpoint property like so:
''' VB.NET
'' Creating the binding obj
Dim objMyBinding As New System.ServiceModel.WSHttpBinding()
objMyBinding.Name = "YOUR_BINDING_NAME"
'' Other details of binding added here ''
'' Creating the endpoint address obj
Dim objMyEndpoint As System.ServiceModel.EndpointAddress
objMyEndpoint = New System.ServiceModel.EndpointAddress(New Uri(ENDPOINT_ADDR_HERE))
'' Creating the Service obj
Dim objWebServices As CalculatorService = New CalculatorService(objMyBinding, objMyEndpoint)
'' Finally adding the behavior to the Service Endpoint (CustomEndpointBehavior is implementation of IEndpointBehavior)
objWebServices.Endpoint.EndpointBehaviors.Add(New CustomEndpointBehavior)
/// C#.NET
// Creating the binding obj
System.ServiceModel.WSHttpBinding objMyBinding = New System.ServiceModel.WSHttpBinding();
objMyBinding.Name = "YOUR_BINDING_NAME";
// Other details of binding added here //
// Creating the endpoint address obj
System.ServiceModel.EndpointAddress objMyEndpoint;
objMyEndpoint = New System.ServiceModel.EndpointAddress(New Uri(ENDPOINT_ADDR_HERE));
// Creating the Service obj
CalculatorService objWebServices = New CalculatorService(objMyBinding, objMyEndpoint);
// Finally adding the behavior to the Service Endpoint (CustomEndpointBehavior is implementation of IEndpointBehavior)
objWebServices.Endpoint.EndpointBehaviors.Add(New CustomEndpointBehavior());
Here's my scenario:
I'm sending an Azure ServiceBus Queue message from Node.js using the node azure sdk like so:
var message = {
body: JSON.stringify({ foo: 'Bar' })
};
serviceBusService.sendQueueMessage('myQueue', message, function (error) {
if (!error) {
console.log('msessage sent');
}
});
I have a c# worker role that is listening to the Queue:
QueueClient Client = QueueClient.CreateFromConnectionString(connStr, QueueName);
Client.OnMessage((receivedMessage) =>
{
var body = receivedMessage.GetBody<string>();
});
When the GetBody method gets executed, i get the following error:
There was an error deserializing the object of type System.String. The input source is not correctly formatted
After some digging around, i found THIS article that helped me get a solution:
Client.OnMessage((receivedMessage) =>
{
var bodyJson = new StreamReader(receivedMessage.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
var myMessage = JsonConvert.DeserializeObject<MyMessage>(bodyJson);
});
If anyone has faced this issue and found a better solution, please let me know!
Thanks!
To anyone who found this question if they were getting this error from sending the message using Service Bus Explorer (like me).
Make sure you specify the correct message type in the drop down:
Thanks for the update, I was doing the reverse and this helped me. I thought I'd add to your solution for completeness. The DeserializeObject method needs the "MyMessage" class defining. In your original post, your JSON is:
{ foo: 'Bar' }
If we drop that into json2csharp (json2csharp.com) we now have the class required to complete your solution:
public class MyMessage
{
public string foo { get; set; }
}
Of course, the dependency is having Newtonsoft.Json package added to your Visual Studio solution:
Install-Package Newtonsoft.Json -Pre
Using the nuget package: Microsoft.Azure.ServiceBus
The following info is contained inside as as comment:
If a message is only being sent and received using this Microsoft.Azure.ServiceBus
client library, then the below extension methods are not relevant and should not be used.
If this client library will be used to receive messages that were sent using both WindowsAzure.Messaging client library and this (Microsoft.Azure.ServiceBus) library, then the Users need to add a User property Microsoft.Azure.ServiceBus.Message.UserProperties while sending the message. On receiving the message, this property can be examined to determine if the message was from WindowsAzure.Messaging client library and if so use the message.GetBody() extension method to get the actual body associated with the message.
---------------------------------------------- Scenarios to
use the GetBody Extension method: ----------------------------------------------
If message was constructed using the WindowsAzure.Messaging client library as
follows:
var message1 = new BrokeredMessage("contoso"); // Sending a plain string var
message2 = new BrokeredMessage(sampleObject); // Sending an actual customer object
var message3 = new BrokeredMessage(Encoding.UTF8.GetBytes("contoso")); // Sending
a UTF8 encoded byte array object await messageSender.SendAsync(message1); await
messageSender.SendAsync(message2); await messageSender.SendAsync(message3);
Then retrieve the original objects using this client library as follows: (By
default Microsoft.Azure.ServiceBus.InteropExtensions.DataContractBinarySerializer
will be used to deserialize and retrieve the body. If a serializer other than
that was used, pass in the serializer explicitly.)
var message1 = await messageReceiver.ReceiveAsync(); var returnedData1 = message1.GetBody();
var message2 = await messageReceiver.ReceiveAsync(); var returnedData2 = message1.GetBody();
var message3 = await messageReceiver.ReceiveAsync(); var returnedData3Bytes =
message1.GetBody(); Console.WriteLine($"Message3 String: {Encoding.UTF8.GetString(returnedData3Bytes)}");
------------------------------------------------- Scenarios to NOT use the GetBody
Extension method: ------------------------------------------------- If message
was sent using the WindowsAzure.Messaging client library as follows: var message4
= new BrokeredMessage(new MemoryStream(Encoding.UTF8.GetBytes("contoso"))); await
messageSender.SendAsync(message4); Then retrieve the original objects using this
client library as follows: var message4 = await messageReceiver.ReceiveAsync();
string returned = Encoding.UTF8.GetString(message4.Body); // Since message was
sent as Stream, no deserialization required here.
May it help you
With the latest Service Bus client libraries (.NET, JS, Java, Python), you can send message(s) using the JS library like this:
const serviceBusClient = new ServiceBusClient("<connectionstring>");
const sender = serviceBusClient.createSender("<queuename>");
await sender.sendMessages([{
body: {
title: "hello"
}
}]);
Note that .sendMessages takes a list as an input, even if you're just sending one message.
And get the body of the received message using .NET library like this:
await using var client = new ServiceBusClient("<connectionstring>");
ServiceBusReceiver receiver = client.CreateReceiver("<queuename>");
ServiceBusReceivedMessage receivedMessage = await receiver.ReceiveMessageAsync();
string body = receivedMessage.Body.ToString();
Console.WriteLine(body); //prints {"title":"hello"}
I am evaluating ServiceStack at the moment. I am in need to create bunch of RESTful webservices. I have the initial code running, and I am quite happy with that. What I was struggling a bit was how to create a service that could consume POST (or PUT) HTTP request that has data in its body.
I've found this thread on ServiceStack forum (http://groups.google.com/group/servicestack/browse_thread/thread/693145f0c3033795) and folliwng it I've been guided to have a look at the following thread on SO (Json Format data from console application to service stack) but it was not really helpful - it described how to create a request, and not how to create a service that can consume such a HTTP request.
When I tried to pass additional data (in the HTTP message body) my servuce returned following error (HTTP 400):
<TaskResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="">
<ResponseStatus>
<ErrorCode>SerializationException</ErrorCode>
<Message>Could not deserialize 'application/xml' request using ServiceStackMVC.Task'
Error: System.Runtime.Serialization.SerializationException: Error in line 1 position 8.Expecting element 'Task' from namespace 'http://schemas.datacontract.org/2004/07/ServiceStackMVC'..
Encountered 'Element' with name 'Input', namespace ''.
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader reader)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(Stream stream)
at ServiceStack.Text.XmlSerializer.DeserializeFromStream(Type type, Stream stream) in C:\src\ServiceStack.Text\src\ServiceStack.Text\XmlSerializer.cs:line 76
at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest httpReq, Type requestType, String contentType) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\Support\EndpointHandlerBase.cs:line 107</Message>
<StackTrace> at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest httpReq, Type requestType, String contentType) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\Support\EndpointHandlerBase.cs:line 115
at ServiceStack.WebHost.Endpoints.RestHandler.GetRequest(IHttpRequest httpReq, IRestPath restPath) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\RestHandler.cs:line 98
at ServiceStack.WebHost.Endpoints.RestHandler.ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, String operationName) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\RestHandler.cs:line 60</StackTrace>
</ResponseStatus>
</TaskResponse>
This led me to https://github.com/ServiceStack/ServiceStack/wiki/Serialization-deserialization I thought that I will give IRequiresRequestStream a go. At the moment my code is as follows:
public class Task : IRequiresRequestStream
{
public string TaskName { get; set; }
public string bodyData { get; set; }
public override bool Equals(object obj)
{
Task task = obj as Task;
if (task == null)
return false;
return TaskName.Equals(task.TaskName);
}
public override int GetHashCode()
{
return TaskName.GetHashCode();
}
public System.IO.Stream RequestStream
{
get
{
return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(bodyData));
}
set
{
if (value.Length == 0)
{
bodyData = string.Empty;
}
else
{
byte[] buffer = new byte[value.Length];
int bytesRead = value.Read(buffer, 0, (int)value.Length);
bodyData = System.Text.Encoding.UTF8.GetString(buffer);
}
}
}
}
and service itself:
public class TaskService : RestServiceBase<Task>
{
public List<Task> tasks { get; set; }
public override object OnGet(Task request)
{
if (string.IsNullOrEmpty(request.TaskName))
{
if (tasks == null || tasks.Count == 0)
return "<tasks/>";
StringBuilder sb = new StringBuilder();
sb.AppendLine("<tasks>");
foreach (Task t in tasks)
{
sb.AppendFormat(" <task id={0}><![CDATA[{2}]]><task/>{1}", t.TaskName, System.Environment.NewLine, t.bodyData);
}
sb.AppendLine("</tasks>");
return sb.ToString();
}
else
{
if (tasks.Contains(request))
{
var task = tasks.Where(t => t.TaskName == request.TaskName).SingleOrDefault();
return String.Format("<task id={0}><![CDATA[{2}]]><task/>{1}", task.TaskName, System.Environment.NewLine, task.bodyData);
}
else
return "<task/>";
}
}
public override object OnPost(Task request)
{
if (tasks.Contains( request ))
{
throw new HttpError(System.Net.HttpStatusCode.NotModified, "additional information");
}
tasks.Add(new Task() { TaskName = request.TaskName, bodyData = request.bodyData });
return null;
}
}
My routes:
Routes.Add<Task>("/tasks/{TaskName}").Add<Task>("/tasks");
It works but... as I couldn't find any similar example I would like to ask if this is the correct way of creating a service that is capable of processing POST requests that have additional informaion included in their message body. Am I doing anything wrong? Is there anything that I've missed?
It was also mentioned on the SO thread link to which I have provided, that using DTO is the preferred way to pass data to ServiceStack based service. Assuming that client needs to send a lot of data, how could we achieve that? I don't want to pass data as JSON object in the URI. Am I making any false assumption here?
Client is not going to be written in C# / .Net. Completely different technology is going to be used. This was one of the reasony why RESTful webservices.
I know returning xml as string may not be the best idea. At the moment it is just a sample code. This will be changed later on.
The most important part is if the solution provided for me is the proper way to create a webservice that can consume HTTP request that has xml data attached in its body. What I've shared with you works I am just not 100% sure that this is the best way to achieve my goal.
Edited on Thursday 8th of March, 2012:
After reading the answer and the comments I've changed my code a little bit. I was pretty sure that if I wanted to use serialization I had to use namespaces (when passing data in the HTTP message body to the service).
I've used http://localhost:53967/api/servicestack.task/xml/metadata?op=Task to get more information about the service I've created.
REST Paths:
All Verbs /tasks/{TaskName}
All Verbs /tasks
HTTP + XML:
POST /xml/asynconeway/Task HTTP/1.1
Host: localhost
Content-Type: application/xml
Content-Length: length
<Task xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ServiceStackMVC">
<AuxData>String</AuxData>
<TaskName>String</TaskName>
</Task>
What I wanted to check was if it was possible to "mix" REST URI and pass the rest of the data as an xml.
Using Fiddler, I've created following POST request:
POST http://localhost:53967/api/tasks/22
Request headers:
User-Agent: Fiddler
Host: localhost:53967
Content-Type: application/xml
Content-Length: 165
Request Body:
<Task xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ServiceStackMVC">
<AuxData>something</AuxData>
</Task>
My DTO right now is as follows:
public class Task
{
public string TaskName { get; set; }
public string AuxData { get; set; }
public override bool Equals(object obj)
{
Task task = obj as Task;
if (task == null)
return false;
return TaskName.Equals(task.TaskName);
}
public override int GetHashCode()
{
return TaskName.GetHashCode();
}
}
And my service code is:
public class TaskService : RestServiceBase<Task>
{
public List<Task> tasks { get; set; }
public override object OnGet(Task request)
{
return tasks;
}
public override object OnPost(Task request)
{
if (tasks.Contains( request ))
{
throw new HttpError(System.Net.HttpStatusCode.NotModified, "additional information");
}
tasks.Add(new Task() { TaskName = request.TaskName });
return null;
}
}
So is this the proper way of passing XML data to the service? I think I am quite happy with xml namespaces included - that makes it even easier to develop services.
Nope, returning an xml string it's not the recommended approach since any string returned gets written directly to the response stream so the service will only work with XML services and not all the other endpoints.
The ServiceStack Way
Is to keep your DTOs that you define your web services with in their own largely dependency-free assembly (I generally will only have a reference the impl and dep-free ServiceStack.Interfaces.dll). You can then re-use these DTOs with ServiceStack's generic Service Clients to get a succinct, typed, end-to-end API without any code-gen.
Different built-in Service Clients
Your C#/.NET clients only need to use the Service Clients contained in the
ServiceStack.Common NuGet package which just contains the ServiceStack.Text.dll, ServiceStack.Interfaces.dll and ServiceStack.Common.dll for full .NET and Silverlight 4/5 client builds.
ServiceStack.Common contains the following Service Clients:
JsonServiceClient - The lightweight, ubiquitous, self-describing resilient format
JsvServiceClient - Faster more compact than JSON ideal for .NET to .NET services
XmlServiceClient - For folks who like using XML (Slower than JSON/JSV)
Soap11ServiceClient / Soap12ServiceClient - If your company mandates the use of SOAP.
If you Install the ProtoBuf Format plugin you also have the option to use the ProtoBufServiceClient which is the fastest binary serializer for .NET.
Easy to swap, easy to test
The C# Service Clients share the same IServiceClient and IRestClient interfaces making it easy to swap out if you want to take advantage of a superior format. Here's an example taking advantage of this where the same Unit Test is also used as a JSON, XML, JSV and SOAP integration test.
By default out-of-the-box, ServiceStack makes all your services available via pre-defined routes in the following convention:
/api/[xml|json|html|jsv|csv]/[syncreply|asynconeway]/[servicename]
This is what the Service Clients use when you use the Send<TResponse> and SendAsync<TResponse> API methods which allows you to call your web services without having to define any custom routes, e.g:
var client = new JsonServiceClient();
var response = client.Send<TaskResponse>(new Task());
If you want you can use the Get, Post, Put, Delete API methods that allows you to specify a url so you can call web services using your custom routes, e.g:
Async API example
FilesResponse response;
client.GetAsync<FilesResponse>("files/", r => response = r, FailOnAsyncError);
Sync API example
var response = client.Get<FilesResponse>("files/README.txt");
Here are some Sync and Async API examples from the RestFiles example project.
XML and SOAP issues
Generally XML and SOAP are more strict and brittle compared to the other formats, to minimize interop issues and reduce payload bloat you should set a global XML Namespace for all your DTOs by adding an Assembly attribute in your DTO Assembly.cs file, e.g:
[assembly: ContractNamespace("http://schemas.servicestack.net/types",
ClrNamespace = "MyServiceModel.DtoTypes")]
If you want to use a different ContractNamespace than the above you will also need to also set it in the EndpointHostConfig.WsdlServiceNamespace if you wish to make use of the SOAP endpoints.
Here are some more versioning tips when developing SOAP/XML web services:
https://groups.google.com/d/msg/servicestack/04GQLsQ6YB4/ywonWgD2WeAJ
SOAP vs REST
Since SOAP routes all requests through the HTTP POST verb, if you wish to make each service available via SOAP as well you will need to create a new class per service and define custom REST-ful routes to each service as described here.
Due to the brittleness, bloated payload size and slower perf of SOAP/XML, it is recommended to use either the JSON, JSV or ProtoBuf formats/endpoints.
Request Model Binders
Another alternative to using IRequiresRequestStream is to use Request Model Binders you can define in your AppHost, e.g:
base.RequestBinders[typeof(Task)] = httpReq => ... requestDto;
C# Client Recommendation
The recommendation is to use ServiceStack's built-in service clients for C# clients although if you wish to use your own HttpClient, than still using the XmlServiceClient will come in handy as you can use Fiddler to see the exact wire-format ServiceStack expects.
I'm building a web api using WCF web api preview 6, currently I'm stuck with a little problem. I would like to have an operation handler to inject an IPrincipal to the operation in order to determine which user is making the request. I already have that Operation Handler and is already configured. But I noticed that when I decorate the operation with the WebInvoke attribute and simultaneously the operation receives an IPrincipal and other domain object, the system throws an exception telling me:
The HttpOperationHandlerFactory is unable to determine the input parameter that should be associated with the request message content for service operation 'NameOfTheOperation'. If the operation does not expect content in the request message use the HTTP GET method with the operation. Otherwise, ensure that one input parameter either has it's IsContentParameter property set to 'True' or is a type that is assignable to one of the following: HttpContent, ObjectContent1, HttpRequestMessage or HttpRequestMessage1.
I do not know what is happening here. To give you some background I'll post some of my code to let you know how am I doing things.
The operation:
[WebInvoke(UriTemplate = "", Method = "POST")]
[Authorization(Roles = "")]
public HttpResponseMessage<dto.Diagnostic> RegisterDiagnostic(dto.Diagnostic diagnostic, IPrincipal principal)
{
......
}
WCF web api knows when to inject the IPrincipal because I decorate the operation with a custom Authorization attribute.
The configuration in the Global file:
var config = new WebApiConfiguration() {EnableTestClient = true};
config.RegisterOAuthHanlder(); //this is an extension method
routes.SetDefaultHttpConfiguration(config);
routes.MapServiceRoute<MeasurementResource>("Measurement");
routes.MapServiceRoute<DiagnosticResource>("Diagnostic");
Then the RegisterOAuthHandler method adds an operation handler to the operation if it's been decorated with the custom authorization attibute. this is how it looks:
public static WebApiConfiguration RegisterOAuthHanlder(this WebApiConfiguration conf)
{
conf.AddRequestHandlers((coll, ep, desc) =>
{
var authorizeAttribute = desc.Attributes.OfType<AuthorizationAttribute>().FirstOrDefault();
if (authorizeAttribute != null)
{
coll.Add(new OAuthOperationHandler(authorizeAttribute));
}
});
return conf;
}
public static WebApiConfiguration AddRequestHandlers(
this WebApiConfiguration conf,
Action<Collection<HttpOperationHandler>, ServiceEndpoint, HttpOperationDescription> requestHandlerDelegate)
{
var old = conf.RequestHandlers;
conf.RequestHandlers = old == null ? requestHandlerDelegate : (coll, ep, desc) =>
{
old(coll, ep, desc);
};
return conf;
}
Can somebody help me with this? Thank you in advanced!
Try wrapping your Diagnostic param in ObjectContent i.e. ObjectContent<Diagnostic>. Then you will use the ReadAs() method to pull out the object.
It should work.