I am attempting to send a stream using RabbitMQ and Servicestack (v1.0.41 using .NET Core).
My Request implements ServiceStack.Web.IRequiresRequestStream, and the stream property is set in the client, but when it gets to the server, the stream is NULL.
Complete Repo
Server Code:
using System;
using System.IO;
using System.Threading.Tasks;
using Funq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using ServiceStack;
using ServiceStack.Messaging;
using ServiceStack.RabbitMq;
using ServiceStack.Web;
namespace Server
{
class Program
{
public static void Main(string[] args)
{
IWebHost host = new WebHostBuilder()
.UseServer(new RabbitServer())
.UseStartup<Startup>()
.Build();
host.Run();
}
}
public class RabbitServer : IServer
{
public void Dispose(){}
public void Start<TContext>(IHttpApplication<TContext> application){}
public IFeatureCollection Features { get; } = new FeatureCollection();
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseServiceStack((AppHostBase)Activator.CreateInstance<AppHost>());
app.Run((RequestDelegate)(context => (Task)Task.FromResult<int>(0)));
}
}
public class AppHost : AppHostBase
{
public AppHost()
: base("My Test Service", typeof(MyService).GetAssembly())
{
}
public override void Configure(Container container)
{
var mqServer = new RabbitMqServer("127.0.0.1");
container.Register<IMessageService>(mqServer);
mqServer.RegisterHandler<HelloRequest>(ExecuteMessage);
mqServer.Start();
}
}
public class MyService : Service
{
public HelloResponse Any(HelloRequest request)
{
Console.WriteLine($"Stream is null: {request.RequestStream == null}");
return new HelloResponse { Counter = request.Counter };
}
}
public class HelloRequest : IReturn<HelloResponse>, IRequiresRequestStream
{
public int Counter { get; set; }
public Stream RequestStream { get; set; }
}
public class HelloResponse
{
public int Counter { get; set; }
}
}
Client Code:
using ServiceStack;
using ServiceStack.Messaging;
using ServiceStack.RabbitMq;
using ServiceStack.Web;
using System;
using System.IO;
using System.Text;
namespace Client
{
class Program
{
static void Main(string[] args)
{
RabbitMqServer messageService = new RabbitMqServer("127.0.0.1");
RabbitMqQueueClient mqClient = messageService.MessageFactory.CreateMessageQueueClient() as RabbitMqQueueClient;
var responseQueueName = mqClient.GetTempQueueName();
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes("Hello World!")) { Position = 0 };
HelloRequest request = new HelloRequest { Counter = 100, RequestStream = ms }; //Counter is just some arbitary extra data
Guid messageId = Guid.NewGuid();
mqClient.Publish(QueueNames<HelloRequest>.In, new Message<HelloRequest>(request) { ReplyTo = responseQueueName, Id = messageId });
}
}
public class HelloRequest : IReturn<HelloResponse>, IRequiresRequestStream
{
public int Counter { get; set; }
public Stream RequestStream { get; set; }
}
public class HelloResponse
{
public int Counter { get; set; }
}
}
Note: I realise I could just use a byte[] in my request object, but I would quite like to make use of the provided IRequiresRequestStream interface so I can switch back to using HTTP rather than AMQP in the future.
I should also say, that I probably won't be using the RabbitMQ Client provided by servicestack, as I am writing custom logic to convert from HTTP to AMQP, so I will be building the rabbitMQ request manually - the code above just demonstrates the problem I am having in the simplest way possible.
I'm going to assume that this won't just work out of the box with AMQP (as it does with HTTP) - so I was thinking that I need to do something like serialize the stream to a byte[] and include it in the RabbitMQ message and then populate the dto which ServiceStack magically re-hydrates on the Server.
So two questions really...
1. Am I on the right track?
2. If so, how do I hook into the de-serialization code on the server so that I have access to the raw RabbitMQ message in order to convert my byte[] back to a stream and set the stream on my dto?
You can't send a IRequiresRequestStream Request DTO into a MQ because it's not a normal serialized Request DTO, instead it instructs ServiceStack to skip deserializing the Request DTO and instead inject the HTTP Request Stream so the Service can perform its own Deserialization instead, this is different to a normal Request DTO which is serialized and can be sent as the body in an MQ Message.
One option if you want to share implementation between a IRequiresRequestStream Service and a Service that can be called by MQ is to just delegate to a common Service that accepts bytes, e.g:
//Called from HTTP
public object Any(HelloStream request) =>
Any(new HelloBytes { Bytes = request.RequestStream.ReadFully() });
//Called from HTTP or MQ
public object Any(HelloBytes request)
{
//= request.Bytes
}
Related
Hi everyone I'm new to c# and I've done my first 2 weeks in this language, so my knowlege is pretty basic.
I'm playing with an app that connects to a client (League of legends client) and uses varius methods to send and get info (Get, Post, Put and Delete).
What the program does:
Once the app is started there's a public class that is called when the form is loaded.
public LCU lcu = new LCU(); (I'll add the code of LCU down below) <-- this strats the connection
I can send as many requests as I want, here's a working example:
var request = await lcu.http_client.DeleteAsync(lcu.baseURL + "/lol-lobby/v2/lobby").ConfigureAwait(true);
My problem is that when I make too many requests (every 2 seconds or below), the app is disconnected from the client/api and to fix that I would need a task that reconnects.
Now I'm not sure how to do that, I've tried adding LCU lcu = new LCU(); inside a timer, but that didn't work.
I would love to know why it didn't work and if you have some suggestions on how to do it, I'll be happy to know.
Thank you!!
LCU.cs (not the main form)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace LeaguePW5
{
public class LCU
{
public string address { get; set; }
public int port { get; set; }
public string username { get; set; }
public string password { get; set; }
public string protocol { get; set; }
public string process_name { get; set; }
public int process_id { get; set; }
public string baseURL => string.Format("{0}://{1}:{2}", this.protocol, this.address, this.port);
public LCU()
{
Process[] process = Process.GetProcessesByName("LeagueClientUx");
if (process.Length != 0)
{
string lockFile;
using (FileStream stream = File.Open(Path.Combine(Path.GetDirectoryName(process[0].MainModule.FileName), "lockfile"), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
lockFile = new StreamReader(stream).ReadToEnd();
}
string[] parameters = lockFile.Split(new string[] { ":" }, StringSplitOptions.None);
this.username = "riot";
this.address = "127.0.0.1";
this.process_name = parameters[0];
this.process_id = Convert.ToInt32(parameters[1]);
this.port = Convert.ToInt32(parameters[2]);
this.password = parameters[3];
this.protocol = parameters[4];
}
}
public HttpClient http_client
{
get
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
httpClientHandler.ServerCertificateCustomValidationCallback = ((HttpRequestMessage httpRequestMessage, X509Certificate2 cert, X509Chain cetChain, SslPolicyErrors policyErrors) => true);
return new HttpClient(httpClientHandler)
{
DefaultRequestHeaders =
{
Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes("riot:" + this.password)))
}
};
}
set
{
}
}
}
}
P.S. If you need the full code, I'll be happy to share it
You have a mistake in http_client initialization. It returns new instance every time you're making the request.
Per HttpClient documentation:
// HttpClient is intended to be instantiated once per application, rather than per-use.
Try this fix and you'll not be disconnected. (in addition I've applied naming policy for property, widely used by Microsoft in .NET)
private HttpClient _httpClient; // backing field
public HttpClient HttpClient
{
get
{
if (_httpClient == null) // create new instance only if still not created
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
httpClientHandler.ServerCertificateCustomValidationCallback = ((HttpRequestMessage httpRequestMessage, X509Certificate2 cert, X509Chain cetChain, SslPolicyErrors policyErrors) => true);
_httpClient = new HttpClient(httpClientHandler)
{
DefaultRequestHeaders =
{
Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes("riot:" + this.password)))
}
};
}
return _httpClient;
}
}
And usage
await lcu.HttpClient.DeleteAsync(lcu.baseURL + "/lol-lobby/v2/lobby").ConfigureAwait(false);
ConfigureAwait(true) is default. Use false or not use ConfigureAwait to avoid a redundant overhead.
Additionally you may derive LCU class from IDisposable and implement the interface because HttpClient is IDisposable. And call HttpClient.Dispose() in the disposing method. But it makes sense only if you create new LCU() class multiple times.
12 hours ago (around dinner time here in Texas), it was working just fine at a moment when I hope the latency to be very high because of the high traffic.
Any idea why this could be happening and how to troubleshoot this issue?
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Nancy.Json;
namespace Training.Threading
{
class TasksDemo
{
static async Task Main(string[] args)
{
Post[] posts = await GetPostsAsync();
foreach(Post post in posts)
System.Console.WriteLine(post.Title);
}
public static Task<Post[]> GetPostsAsync() => Task.Run(() =>
{
var json = new WebClient().DownloadString("https://jsonplaceholder.typicode.com/posts");
JavaScriptSerializer ser = new JavaScriptSerializer();
var posts = ser.Deserialize<Post[]>(json);
return posts;
});
}
public class Post
{
public int UserId { get; set; }
public string Title { get; set; }
public string Body { get; set; }
}
}
I don't think the code is the issue here. It is more so your ISP or the bandwith available to the server / traffic you are trying to contact.
Some tools that will help you diagnose the issue specifically in windows to spot your issue include
'tracert' (trace route)
'ping' (ping)
In Windows:
ping jasonplaceholder.typicode.com
To view latency between "hops"
tracert jsonplaceholder.typicode.com
this will provide in milliseconds the latency to the server you are trying to reach assuming they respond to ping request ICMP.
Following the advice fo #maccettura and #ckuri I'm using now HttpClient, I thought I'd be good to post the code here.
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Nancy.Json;
namespace Training.Threading
{
class TasksDemo
{
static async Task Main(string[] args)
{
string json = await GetPostsAsync();
// System.Console.WriteLine(json);
JavaScriptSerializer ser = new JavaScriptSerializer();
var posts = ser.Deserialize<Post[]>(json);
foreach(Post post in posts)
System.Console.WriteLine(post.Title);
}
public static async Task<string> GetPostsAsync()
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(#"https://jsonplaceholder.typicode.com");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.GetStringAsync("/posts");
}
}
}
public class Post
{
public int UserId { get; set; }
public string Title { get; set; }
public string Body { get; set; }
}
}
Forgive my lack of knowlegde, I'm a database guy although I dabbled a bit with C# a while back. I am trying to figure how to get this API running.
The API I'm trying to consume is from https://rapidapi.com/api-sports/api/api-nba/. There is barely any documentation to guide me.
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using unirest_net.http;
using unirest_net;
namespace NBA_test
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Start ...");
Task<HttpResponse<MyClass.RootObject>> response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/5162")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJsonAsync<MyClass.RootObject>();
var status = response.Status;
Console.WriteLine("End ....");
}
}
public class MyClass
{
public class Result
{
public string seasonYear { get; set; }
public int gameId { get; set; }
public string arena { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
}
}
var status goes from Created to Running and then that's it, program closes. No error message but I don't know how to get the JSON out of this API. I know I'm missing something but don't know what.
You are in a console application with a sync main method. You should not call an async method inside a sync method. I made your async call into a sync call :
public static void Main(string[] args)
{
Console.WriteLine("Start ...");
var response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/5162")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJson<RootObject>();
var status = response.Status;
Console.WriteLine("End ....");
}
you still might ask where is your deserialized JSON?
According to Unirest docs:
Response
Upon recieving a response Unirest returns the result in the
form of an Object, this object should always have the same keys for
each language regarding to the response details.
.Code - HTTP Response Status Code (Example 200)
.Headers - HTTP
Response Headers
.Body - Parsed response body where applicable, for
example JSON responses are parsed to Objects / Associative Arrays.
.Raw - Un-parsed response body
Basically, you can access your result like this:
if (response.Code == 200) // Success, OK in HTTP Codes
{
response.Body; // which body has the type of MyClass.RootObject
}
The complete example:
public static void Main(string[] args)
{
Console.WriteLine("Start ...");
var response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/5162")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJson<RootObject>();
if (response.Code == 200) // Success, OK in HTTP Codes
{
response.Body; // which body has the type of MyClass.RootObject
}
Console.WriteLine("End ....");
Console.ReadLine(); // to force command line stay up for an input and not close applicaiton immediately aftter runing it.
}
Update 1:
Here is a live and working of Unirest on .NET Fiddle:
https://dotnetfiddle.net/EZDopa
You can use httpClient
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
var response = httpClient.GetStringAsync(new Uri(url)).Result;
var releases = JArray.Parse(response);
}
As the others pointed out you need to add some kinda wait statment. I did many calls to api that took time to process in one of my applications. Based on the others comments I updated to allow for logic to be executed while your waiting for the call to come back. an edit for your code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using unirest_net.http;
using unirest_net;
namespace NBA_test
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Start ...");
//the added Wait() causes the thread to hold until the task is finished.
Task<HttpResponse<MyClass.RootObject>> response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/5162")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJsonAsync<MyClass.RootObject>();
//if need to perform other logic while you are waiting
while(response.Status == TaskStatus.Running)
{
// perform other logic like gui here
}
var status = response.Status;
Console.WriteLine("End ....");
}
}
public class MyClass
{
public class Result
{
public string seasonYear { get; set; }
public int gameId { get; set; }
public string arena { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
}
}
Turns out the API's documentation is flawed. I managed to make it work by simply using string (and Newtonsoft's Json.NET). Thx for the help #AliBahrami. Code looks like this now:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using unirest_net.http;
using unirest_net;
using Newtonsoft.Json.Linq;
namespace NBA_test
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Start of Program...");
HttpResponse<string> response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/9999")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJson<string>();
var result = response.Body;
JObject parsedString = JObject.Parse(result);
RootObject myGame = parsedString.ToObject<RootObject>();
// Get game id
Console.WriteLine(myGame.results[0].gameId);
Console.WriteLine("End of Program....");
}
}
}
I am using Microsoft.AspNetCore.SignalR nuget package with Bazinga.AspNetCore.Authentication.Basic which adds basic authentication to dotnet core. My C# SignalR client connects when there is no authentication, but when I add AuthorizeAttribute it connects by http and http request header gets authenticated successfully but the Socket does not authenticate probably because there is no header in socket messages.
So I am wondering how should I pass a token or something to authenticated socket connection or is there a example code that I can follow. I think I should pass a random token to just authenticated user and the user needs to constantly pass the token in messages.
Client project, Server project
Server:
using System.Threading.Tasks;
using Bazinga.AspNetCore.Authentication.Basic;
using Domainlogic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace API
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowAnyOrigin();
}));
services.AddSignalR();
services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
.AddBasicAuthentication(credentials => Task.FromResult(
credentials.username == "SomeUserName"
&& credentials.password == "SomePassword"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
app.UseCors(CorsConstants.AnyOrigin);
app.UseFileServer();
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
app.UseAuthentication();
}
}
}
Server hub:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
namespace Domainlogic
{
public class MessagePayload
{
public string Name { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
}
[Authorize]
public class MessageHub : Hub
{
// connected IDs
private static readonly HashSet<string> ConnectedIds = new HashSet<string>();
public override async Task OnConnectedAsync()
{
ConnectedIds.Add(Context.ConnectionId);
await Clients.All.SendAsync("SendAction", "joined", ConnectedIds.Count);
}
public override async Task OnDisconnectedAsync(Exception ex)
{
ConnectedIds.Remove(Context.ConnectionId);
await Clients.All.SendAsync("SendAction", "left", ConnectedIds.Count);
}
public async Task Send(MessagePayload message)
{
await Clients.All.SendAsync("SendMessage", message);
}
}
}
Client:
using System;
using System.Net;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Connections.Client;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
namespace SignalRClient
{
public class MessagePayload
{
public string Name { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
}
class Program
{
public static string Base64Encode(string plainText) {
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
static void Main(string[] args)
{
var credential = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes("SomeUserName" + ":" + "SomePassword"));
//Set connection
var connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5000/chat", options =>
{
options.Headers.Add("Authorization", $"Basic {credential}");
})
.AddJsonProtocol()
.Build();
connection.On<MessagePayload>("SendMessage", param =>
{
Console.WriteLine(param.Message);
});
connection.StartAsync().Wait();
var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromSeconds(3);
int i = 0;
var timer = new System.Threading.Timer((e) =>
{
connection.InvokeAsync<MessagePayload>("Send", new MessagePayload()
{
Message = "Some message: " + i++
});
}, null, startTimeSpan, periodTimeSpan);
Console.Read();
connection.StopAsync();
}
}
}
Thanks to "davidfowl" on GitHub, the solution was moving UseAuthentication above the UseSignalR.
Source: https://github.com/aspnet/SignalR/issues/2316
Instead of:
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
app.UseAuthentication();
Use this:
app.UseAuthentication();
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
To solve this I had to store in server cache with a Dictionary the token of the user with the connectionId. That because in the hub I don't have control over the session.
So every time a user is connected with the hub I exposed and endpoint which receives the connectionId and the token of the user. When a request is handle by the Hub I know by the connection which is the user authenticated.
Controller
[HttpPost]
[Authorize]
[Route("{connectionId}")]
public IActionResult Post(string connectionId)
{
this.hubConnectionService.AddConnection(connectionId, this.workContext.CurrentUserId);
return this.Ok();
}
Hub
public override Task OnDisconnectedAsync(Exception exception)
{
this.hubConnectionService.RemoveConnection(this.Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
I have a web service that service an Excel file
public class ReportService : Service
{
public IReportRepository Repository {get; set;}
public object Get(GetInvoiceReport request)
{
var invoices = Repository.GetInvoices();
ExcelReport report = new ExcelReport();
byte[] bytes = report.Generate(invoices);
return new FileResult(bytes);
}
}
and I setup the object that is retured from the service as
public class FileResult : IHasOptions, IStreamWriter, IDisposable
{
private readonly Stream _responseStream;
public IDictionary<string, string> Options { get; private set; }
public BinaryFileResult(byte[] data)
{
_responseStream = new MemoryStream(data);
Options = new Dictionary<string, string>
{
{"Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{"Content-Disposition", "attachment; filename=\"InvoiceFile.xlsx\";"}
};
}
public void WriteTo(Stream responseStream)
{
if (_responseStream == null)
return;
using (_responseStream)
{
_responseStream.WriteTo(responseStream);
responseStream.Flush();
}
}
public void Dispose()
{
_responseStream.Close();
_responseStream.Dispose();
}
}
Now, the webservice works fine when tested through a browser; but it gives an error message when tested from a unit test. Below is the error message:
System.Runtime.Serialization.SerializationException : Type definitions
should start with a '{', expecting serialized type 'FileResult', got
string starting with:
PK\u0003\u0004\u0014\u0000\u0008\u0000\u0008\u0000�\u000b5K���%\u0001\u0000\u0000�\u0003\u0000\u0000\u0013\u0000\u0000\u0000[Content_Types].xml��
at
ServiceStack.Text.Common.DeserializeTypeRefJson.StringToType(TypeConfig
typeConfig, StringSegment strType, EmptyCtorDelegate ctorFn,
Dictionary2 typeAccessorMap) at
ServiceStack.Text.Common.DeserializeType1.<>c__DisplayClass2_0.b__1(StringSegment value) at ServiceStack.Text.Json.JsonReader1.Parse(StringSegment
value) at ServiceStack.Text.Json.JsonReader1.Parse(String value)
at ServiceStack.Text.JsonSerializer.DeserializeFromString[T](String
value) at
ServiceStack.Text.JsonSerializer.DeserializeFromStream[T](Stream
stream) at
ServiceStack.ServiceClientBase.GetResponse[TResponse](WebResponse
webResponse) at
ServiceStack.ServiceClientBase.Send[TResponse](String httpMethod,
String relativeOrAbsoluteUrl, Object request)
Below is the unit test I used to test the webservice:
[Test]
public void TestInvoiceReport()
{
var client = new JsonServiceClient("http://localhost/report/");
var authResponse = client.Send(new Authenticate
{
provider = CredentialsAuthProvider.Name,
UserName = "[User Name]",
Password = "[Password]"
});
var requestDTO = new GetInvoiceReport();
var ret = client.Get<FileResult>(requestDTO);
Assert.IsTrue(ret != null);
}
Edit:
I am including the definition for my request DTO class:
[Route("/invoices", "GET")]
public class GetInvoiceReport: IReturn<FileResult>
{
}
Any help is appreciated.
Note: if you're making a HTTP Request instead of calling the Service in code, it's an Integration Test instead of a Unit Test.
You haven't provided your GetInvoiceReport Request DTO definition, but if you're returning anything that's not a serialized DTO it should be specified it its IReturn<T> interface, e.g:
public class GetInvoiceReport : IReturn<byte[]> { ... }
Then you'll be able to download the raw bytes with:
byte[] response = client.Get(new GetInvoiceReport());
You can use the Service Clients Request Filters for inspecting the HTTP Response Headers.
I'd also recommend checking out ServiceStack's .NET Service Clients docs which contains extensive info for downloading raw Responses.