No MediaTypeFormatter is available on Windows 2003 Server - windows-server-2003

I have a pretty straight forward ApiController which works fine on Win7 but on Windows 2003 Server I get an error.
The get request (from either the browser or $.getJson):
https://site.com:61656/AD/Authenticate?UserName=xxxx&Password=xxxxx&AuthKey=xxxxxx
I get the following error:
<Exception xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/System.Web.Http.Dispatcher">
<ExceptionType>System.InvalidOperationException</ExceptionType>
<Message>
No MediaTypeFormatter is available to read an object of type 'InputModel' from content with media type ''undefined''.
</Message>
<StackTrace>
at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger) at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger) at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) at System.Web.Http.Controllers.HttpActionBinding.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(HttpParameterBinding parameterBinder) at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() at System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1 enumerator, CancellationToken cancellationToken)
</StackTrace>
</Exception>
I am using the 5/1/2012 nuget nightly build packages. It looks like the objectContent.Value is coming through null on Windows 2003 Server but not on Windows 7 in the following line in HttpContentExtensions.cs:
if (objectContent != null && objectContent.Value != null && type.IsAssignableFrom(objectContent.Value.GetType()))
{
return TaskHelpers.FromResult((T)objectContent.Value);
}
The controller action:
[AcceptVerbs("GET", "POST")]
public ResultModel Authenticate(InputModel inputModel)
{
var test = ControllerContext.Request.Content.Headers.ContentType;
//Console.WriteLine(test.MediaType);
try
{
Console.WriteLine("AD Authorize request received: " + inputModel.UserName);
var ldap = new LdapAuthentication();
return ldap.Authenticate(inputModel);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return new ResultModel();
}
}
The MediaType comes through null on Win7 but on Windows 2003 server the request never makes it to the controller action.
Is there a way to specify a default media formatter which would handle the "'undefined'" media type?
EDIT:
Here is the input model:
public class InputModel {
public string UserName { get; set; }
public string Password { get; set; }
public string AuthKey { get; set; }
}
And here is the (self host) config:
var config = new HttpsSelfHostConfiguration(ConfigurationManager.AppSettings["serviceUrl"]);
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Routes.MapHttpRoute("default", "{controller}/{action}");
var server = new HttpSelfHostServer(config);
try
{
server.OpenAsync().Wait();
Console.WriteLine("Waiting...");
EDIT #2:
I have noticed more interesting Win7 vs Windows 2003 Server behavior. If I remove the InputModel parameter on the controller action, the action is invoked while running on both Win7 and Windows 2003 Server. However on Win7 it returns JSON from both GET an POST requests. On Windows 2003 Server it returns XML from GET and JSON from POST.
This led me to testing the InputModel parameter using a POST. I have verified that the action is invoked correctly and the InputModel parameter is bound on Windows 2003 Server but only when using POST. So the workaround would be to grab the parameters manually on a GET. This allows jQuery's $.getJSON to work against a self hosted server under Windows 2003 Server:
[AcceptVerbs("GET")]
public ResultModel Authenticate()
{
try
{
var inputModel = new InputModel();
var query = ControllerContext.Request.RequestUri.ParseQueryString();
inputModel.UserName = query.GetValues("UserName") != null ? query.GetValues("UserName")[0] : null;
inputModel.Password = query.GetValues("Password") != null ? query.GetValues("Password")[0] : null;
inputModel.AuthKey = query.GetValues("AuthKey") != null ? query.GetValues("AuthKey")[0] : null;
Console.WriteLine("AD Authorize request received: " + inputModel.UserName);
var ldap = new LdapAuthentication();
return ldap.Authenticate(inputModel);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return new ResultModel();
}
}

Can you show the configured formatters and also your InputModel class? It looks like the serializer can't handle your class. Do you use interfaces on any of the properties?

Related

Content-Type must be 'application/json-patch+json' JsonServiceClient ServiceStack

I'm trying to perform a patch with a JsonServiceClient to a service stack api as follows:
var patchRequest = new JsonPatchRequest
{
new JsonPatchElement
{
op = "replace",
path = "/firstName",
value = "Test"
}
};
_jsonClient.Patch<object>($"/testurl/{id}", patchRequest);
But I'm getting the following error:
Content-Type must be 'application/json-patch+json'
The error is clear. Is there a way to change the content type before perform the request for the JsonServiceClient?
This is the request POCO in the ServiceStack api:
[Api("Partial update .")]
[Route("/testurl/{Id}”, "PATCH")]
public class PartialTest : IReturn<PartialTestRequestResponse>, IJsonPatchDocumentRequest,
IRequiresRequestStream
{
[ApiMember(Name = “Id”, ParameterType = "path", DataType = "string", IsRequired = true)]
public string Id { get; set; }
public Stream RequestStream { get; set; }
}
public class PartialTestRequestResponse : IHasResponseStatus
{
public ResponseStatus ResponseStatus { get; set; }
}
Service implementation:
public object Patch(PartialTest request)
{
var dbTestRecord = Repo.GetDbTestRecord(request.Id);
if (dbTestRecord == null) throw HttpError.NotFound("Record not found.");
var patch =
(JsonPatchDocument<TestRecordPoco>)
JsonConvert.DeserializeObject(Request.GetRawBody(), typeof(JsonPatchDocument<TestRecordPoco>));
if (patch == null)
throw new HttpError(HttpStatusCode.BadRequest, "Body is not a valid JSON Patch Document.");
patch.ApplyTo(dbTestRecord);
Repo.UpdateDbTestRecord(dbTestRecord);
return new PartialTestResponse();
}
I'm using Marvin.JsonPatch V 1.0.0 library.
It's still not clear where the Exception is coming from as it's not an Error within ServiceStack. If you've registered a Custom Format or Filter that throws this error please include its impl (or a link to it) as well as the full StackTrace which will identify the source of the error.
But you should never call Patch<object> as an object return type doesn't specify what Response Type to deserialize into. Since you have an IReturn<T> marker you can just send the Request DTO:
_jsonClient.Patch(new PartialTest { ... });
Which will try to deserialize the Response in the IReturn<PartialTestRequestResponse> Response DTO. But as your Request DTO implements IRequiresRequestStream it's saying you're expecting unknown bytes that doesn't conform to a normal Request DTO, in which case you likely want to use a raw HTTP Client like HTTP Utils, e.g:
var bytes = request.Url.SendBytesToUrl(
method: HttpMethods.Path,
requestBody: jsonPatchBytes,
contentType: "application/json-patch+json",
accept: MimeTypes.Json);
You could modify the ContentType of a JSON Client using a request filter, e.g:
_jsonClient.RequestFilter = req =>
req.ContentType = "application/json-patch+json";
But it's more appropriate to use a low-level HTTP Client like HTTP Utils for non-JSON Service Requests like this.

trouble shooting System.AggregateException

I need another pair of eyes to take a look at this. It's driving me nuts.
I am intermittently getting a 'System.AggregateException' when running a console app that connects to a web api.
I am doing this in a local testing environment through visual studio(IIS Express).
As stated, I have two different apps running locally on IIS Express(2 different ports). One is a console app and the other is a web api. The console app connects to the web api.
It's about 50/50 if it works or not. 50% of the time it works fine and spits out the expected results. But the other 50% of the time, it fails with the errors below. When it does fail, it's always immediate, like 2 or 3 seconds after starting the console app.
After some Googling and fiddling around with various settings, I know it's not either of these:
not a timeout issue
not a firewall issue
I've tried setting breakpoints at various points, but it never really reveals anything significant.
The exception I get when it fails is:
An exception of type 'System.AggregateException' occurred in mscorlib.dll but was not handled in user code
Here is the inner exception:
No connection could be made because the target machine actively refused it http://localhost:45321
The stack trace indicates:
at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task1.get_Result()
at BeatGenerator.BeatGeneratorMain.<>c.b__2_0(Task1 postTask) in C:\Users\xxx\Documents\VS2012\DrumBeats\BeatGenerator\BeatGeneratorMain.cs:line 72
at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Here is the error line:
var response = await http.PostAsJsonAsync("http://localhost:45321/api/drumcorp/beats/generate", drumbeat)
.ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode());
This is the console app that connects to API controller:
public class DrumBeats
{
public int StartBeat { get; set; }
public int EndBeat { get; set; }
public int ChordId { get; set; }
}
public class BeatGeneratorMain
{
static void Main(string[] args)
{
Generate().Wait();
}
private static async Task Generate()
{
var drumbeat = new DrumBeats();
drumbeat.ChordId = 122;
drumbeat.StartBeat = 2;
drumbeat.EndBeat = 4;
var creds = new NetworkCredential("testUser", "xxxx", "xxx"); //username, pw, domain
var handler = new HttpClientHandler { Credentials = creds };
using (var http = new HttpClient(handler))
{
http.Timeout = TimeSpan.FromMinutes(10);
var response = await http.PostAsJsonAsync("http://localhost:45321/api/drumcorp/beats/generate", drumbeat)
.ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode());
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
}
}
}
This is the relevant section of the web api controller app:
public class DrumBeats //same as in console app
{
public int StartBeat { get; set; }
public int EndBeat { get; set; }
public int ChordId { get; set; }
}
[HttpPost("api/drumcorp/beats/generate")]
public string PostMethodBeats([FromBody] DrumBeats drumbeat)
{
string beatsChart = DrumBeatMaster.ReturnBeatsChart(DrumBeats.ChordId, DrumBeats.StartBeat, DrumBeats.EndBeat);
var mesg = "<b>Beats Created</b><br /><br /> ";
return mesg + beatsChart;
}
DrumBeatMaster.ReturnBeatsChart is just a simple helper method that processes the beats and spits out a string.
To understand what is the exception you will have to catch the aggregate exception and throw them flattened like
try
{
// Your code
}
catch (AggregateException agg)
{
throw agg.Flatten();
}

web api get route template from inside handler

I searched a lot before putting the questions here but the more I search the more confused I get.
So I have created an handler and I am trying to get the route like this:
public class ExecutionDelegatingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (securityAuthority.VerifyPermissionToExecute(request.GetRouteData().Route.RouteTemplate, request.Headers))
{
return base.SendAsync(request, cancellationToken);
}
else
{
httpResponseMessage.StatusCode = HttpStatusCode.Unauthorized;
}
}
}
GetRouteData returns null so I can't get to the RouteTemplate property but I can
see the route there in a list deep in the stack. I found so many different ways which one can use to get the route, but those methods evaluate to null as well. I am a bit lost on how to get something so simple done. I am using self host for development but will use IIS for deployment.
UPDATE 1
I forgot to put here what else I had tried:
//NULL
request.GetRouteData();
//EMPTY
request.GetRequestContext().Configuration.Routes.GetRouteData(request).Route.RouteTemplate;
//EMPTY
request.GetConfiguration().Routes.GetRouteData(request).Route.RouteTemplate;
The route works just fine, but strangely if I try to get the controller to service that request I get a 404... if I just step over that I will get to the controller just fine.
HttpControllerDescriptor httpControllerDescriptor = request.GetRequestContext().Configuration.Services.GetHttpControllerSelector().SelectController(request);
IHttpController httpController = httpControllerDescriptor.CreateController(request);
I am using autofac to discover all the routes which I am defining just like:
[Route("queries/organization/clients")]
[HttpGet]
public ClientInitialScreenModel GetClients()
{
return OrganizationModelsBuilder.GetClientInitialScreen();
}
UPDATE 2
If I GetRouteData gets called after the line above, I am able to get the route template:
base.SendAsync(request, cancellationToken);
var routeData = request.GetRouteData();
So maybe I misunderstood the whole picture and I cant get the route template before the handler that resolves which controller to execute for the request does its work... is that the case?
For reference this is the handler I am working on:
public class ExecutionDelegatingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var securityAuthority = (ISecurityAuthority) request.GetDependencyScope().GetService(typeof (ISecurityAuthority));
var configuration = (IWebApiConfiguration)request.GetDependencyScope().GetService(typeof(IWebApiConfiguration));
var tsc = new TaskCompletionSource<HttpResponseMessage>();
var httpResponseMessage = new HttpResponseMessage();
if (request.RequestUri.AbsolutePath.Equals(configuration.CommandGatewayUrl, StringComparison.InvariantCultureIgnoreCase))
{
var apiMessage = JsonConvert.DeserializeObject<ApiCommandEnvelope>(request.Content.ReadAsStringAsync().Result);
if (securityAuthority != null && !securityAuthority.VerifyPermissionToExecute(apiMessage, request.Headers))
{
httpResponseMessage.StatusCode = HttpStatusCode.Unauthorized;
}
else
{
var messageProcessor = (IWebApiMessageProcessor)request.GetDependencyScope().GetService(typeof(IWebApiMessageProcessor));
var reponse = messageProcessor.HandleRequest(apiMessage);
httpResponseMessage.StatusCode = (HttpStatusCode) reponse.StatusCode;
if (!string.IsNullOrEmpty(reponse.Content))
{
httpResponseMessage.Content = new StringContent(reponse.Content);
}
}
}
else
{
if (securityAuthority != null && !securityAuthority.VerifyPermissionToExecute(request.GetRouteData().Route.RouteTemplate, request.Headers))
{
httpResponseMessage.StatusCode = HttpStatusCode.Unauthorized;
}
else
{
return base.SendAsync(request, cancellationToken);
}
}
tsc.SetResult(httpResponseMessage);
return tsc.Task;
}
UPDATE 3
The code runs fine in a non self hosting environment, so this is more like a self host issue.
The Web Api still has a lot to improve. It was tricky to find a way to get this working and I just hope this saves other guys from spending all the time I did.
var routeTemplate = ((IHttpRouteData[]) request.GetConfiguration().Routes.GetRouteData(request).Values["MS_SubRoutes"])
.First().Route.RouteTemplate;
I had a similar issue, but was able to get the route inside the message handler by the following:
request.GetConfiguration().Routes.GetRouteData(request).Route.RouteTemplate;
The answer from Marco (shown below) is correct so long as there isn't more than one route defined with the same HttpMethod. The .First() will grab the 1st route defined in that specific ApiController, but this doesn't ensure it grabs the correct one. If you use the ControllerContext to get the Route, you can be sure you've got the exact endpoint you want.
Marco's:
var routeTemplate = ((IHttpRouteData[])request.GetConfiguration()
.Routes.GetRouteData(request).Values["MS_SubRoutes"])
.First().Route.RouteTemplate;
The code:
((IHttpRouteData[])request.GetConfiguration()
.Routes.GetRouteData(request).Values["MS_SubRoutes"])
actually returns a collection of IHttpRouteData, and it contains a record for each endpoint which has the same HttpMethod (Post, Get, etc)... The .First() doesn't guarantee you get the one you want.
Guaranteed To Grab Correct Endpoint's RouteTemplate:
public static string GetRouteTemplate(this HttpActionContext actionContext)
{
return actionContext.ControllerContext.RouteData.Route.RouteTemplate;
}
I used an extension method so to call this you'd do:
var routeTemplate = actionContext.GetRouteTemplate();
This will assure that you get the specific RouteTemplate from the endpoint making the call.
I think you can get route Data from request.Properties property and easy to unit test.
/// <summary>
/// Gets the <see cref="System.Web.Http.Routing.IHttpRouteData"/> for the given request or null if not available.
/// </summary>
/// <param name="request">The HTTP request.</param>
/// <returns>The <see cref="System.Web.Http.Routing.IHttpRouteData"/> or null.</returns>
public static IHttpRouteData GetRouteData(this HttpRequestMessage request)
{
if (request == null)
{`enter code here`
throw Error.ArgumentNull("request");
}
return request.GetProperty<IHttpRouteData>(HttpPropertyKeys.HttpRouteDataKey);
}
private static T GetProperty<T>(this HttpRequestMessage request, string key)
{
T value;
request.Properties.TryGetValue(key, out value);
return value;
}
Reference link of code

Enable gzip/deflate compression

I'm using ServiceStack (version 3.9.44.0) as a Windows Service (so I'm not using IIS) and I use both its abilities both as an API and for serving web pages.
However, I haven't been able to find how exactly I should enable compression when the client supports it.
I imagined that ServiceStack would transparently compress data if the client's request included the Accept-Encoding:gzip,deflate header, but I'm not seeing any corresponding Content-Encoding:gzip in the returned responses.
So I have a couple of related questions:
In the context of using ServiceStack as a standalone service (without IIS), how do I enable compression for the responses when the browser accepts it.
In the context of a C# client, how do similarly I ensure that communication between the client/server is compressed.
If I'm missing something, any help would be welcome.
Thank you.
If you want to enable compression globally across your API, another option is to do this:
Add this override to your AppHost:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new MyServiceRunner<TRequest>(this, actionContext);
}
Then implement that class like this:
public class MyServiceRunner<TRequest> : ServiceRunner<TRequest>
{
public MyServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext)
{
}
public override void OnBeforeExecute(IRequestContext requestContext, TRequest request)
{
base.OnBeforeExecute(requestContext, request);
}
public override object OnAfterExecute(IRequestContext requestContext, object response)
{
if ((response != null) && !(response is CompressedResult))
response = requestContext.ToOptimizedResult(response);
return base.OnAfterExecute(requestContext, response);
}
public override object HandleException(IRequestContext requestContext, TRequest request, Exception ex)
{
return base.HandleException(requestContext, request, ex);
}
}
OnAfterExecute will be called and give you the chance to change the response. Here, I am compressing anything that is not null and not already compressed (in case I'm using ToOptimizedResultUsingCache somewhere). You can be more selective if you need to but in my case, I'm all POCO objects with json.
References
ServiceStack New Api
For those interested, a partial answer to my own question, you can use the extension method ToOptimizedResult() or, if you are using caching ToOptimizedResultUsingCache().
For instance, returning a compressed result:
public class ArticleService : Service
{
public object Get(Articles request) {
return base.RequestContext.ToOptimizedResult(
new List<Articles> {
new Article {Ref = "SILVER01", Description = "Silver watch"},
new Article {Ref = "GOLD1547", Description = "Gold Bracelet"}
});
}
}
References
CachedServices.cs example
CompressedResult.cs
Google Group question on Compression in ServiceStack

My servicestack cached service includes extra slashes in the response

I have created a cached webservice using ServiceStack.
public class ContentSearchService : ServiceBase<ContentSearch>
{
public ICacheClient CacheClient { get; set; }
protected override object Run(ContentSearch request)
{
var cacheKey = "unique_key_for_this_request2";
return base.RequestContext.ToOptimizedResultUsingCache(this.CacheClient, cacheKey, () =>
{
//Delegate is executed if item doesn't exist in cache
//Any response DTO returned here will be cached automatically
return new ContentSearchResponse()
{
Contents = new List<ContentData>()
{
new ContentData()
{
FileName = "testfile.jpg"
}
}
};
});
}
}
This is then run using:
IRestClient client = new JsonServiceClient("http://internal");
ContentSearch search = new ContentSearch();
ContentSearchResponse response = client.Put<ContentSearchResponse>("/json/syncreply/ContentSearch", search);
The first response is returned as expected and converted into the response object. The second, which is cached is returned with extra slashes and as a result can't be serialized.
First response:
{"Contents":[{"FileName":"testfile.jpg","Company":0,"Version":0}]}
Second response:
{\"Contents\":[{\"FileName\":\"testfile.jpg\",\"Company\":0,\"Version\":0}]}
I'm using Redis as the cache.
I've had a look at the redis server they are stored with the slashes.
Turns out this was related to the client using a different version of the ServiceStack assemblies. I updated both the server and client to the lastest and everything worked as expected.

Resources