ServiceStack Not Caching Json - servicestack

I'm using ServiceStack on ASP.NET 4.5. I'm having troubles with the ServiceStack InMemory caching. If I just call the URL directly from the browser it pulls back the cached version, but if I try to call it via getJSON in JQuery, it never pulls back the cached version and just refetches the data each time.
Here's the basic code bits...
public class AResponse : IHasResponseStatus
{
public ResponseStatus ResponseStatus { get; set; }
public Html Html { get; set; }
}
public object Get(A request)
{
var cacheKey = UrnId.Create<string>(request.UserKey + request.Id);
var expireInTimespan = new TimeSpan(1, 0, 0);
return RequestContext.ToOptimizedResultUsingCache(base.Cache, cacheKey, expireInTimespan, () =>
{
var ar = new AResponse();
var html = new Html();
html.Test = "test";
ar.Html = html;
return ar;
});
}
...Thanks for any ideas.

My understanding is that when you call the Service from the browser you are going to cache a Html version. So, ServiceStack will insert/retrieve by applying a .html suffix onto your key. When you call it from JQuery it will cache a Json version and apply a .json suffix onto your key. You could test this by calling into your Service from the browser using ?format=json on the url. This would cache a json version (instead of html) and then calling from JQuery to get the cached json.

Related

Unable to get from ServiceStack API using JsonServiceClient

I am trying to get all equipment types from my API using the following code.
client = new JsonServiceClient(environment.apiEndpoint);
var equipmentTypes = new GetEquipmentTypes();
var response = this.client.get(equipmentTypes);
I can see that it is in the network tab. The data is being transferred.
public class GetEquipmentTypeResponse
{
public IEnumerable<EquipmentType> Results { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
Is the return DTO from the API.
[Route("/api/EquipmentTypes", "GET")]
public class GetEquipmentTypes : IReturn<GetEquipmentTypeResponse>
{
}
Is the ServiceInterface used.
IEnumerable<EquipmentType> response = db.Select<EquipmentType>(x=>x.Name == request.Name);
return new GetEquipmentTypeResponse { Results = response,
ResponseStatus = new ResponseStatus { }};
Is what the API returns.
The API is written in asp.net. The client side is angular 6 (typescript).
I have attached two images, which is the request and the response given.
This is the request which is sent to the API.
This is what the API responds.
This is what I get from var response. (console.log(response))
The screenshot shows that the response is being returned fine, the Promise result is just not being awaited, try:
var response = await this.client.get(equipmentTypes);

ServiceStack Render Razor Fails to Find View

This is a self hosted project. There is a Views\Member.cshtml file that is set to copy always as content. The following when run returns null for the razorView. I seem to be missing something here.
var razorView = razor.GetViewPage("Member"); //e.g. /Views/Member.cshtml
var html = razor.RenderToHtml(razorView, em);
Console.WriteLine(html);
Furthermore I've tried this and it returns file not found although the file is there:
var html = razor.RenderToHtml(AppDomain.CurrentDomain.BaseDirectory +
"Views\\" + "Member.cshtml", em);
Also, is there a way to have a service gateway return the rendered razor?
Member.cshtml exists: https://db.tt/xuOSAjEj31
razor: https://db.tt/qeApkAEZGH
AppHost.cs:
Plugins.Add(new RazorFormat() {
// ScanRootPath = AppDomain.CurrentDomain.BaseDirectory + "Views"
} );
I've tested this with the ServiceStack Self Host with Razor project in ServiceStackVS after changing each .cshtml to Copy if newer, changed AppHost.Configure() to just:
public override void Configure(Container container)
{
this.Plugins.Add(new RazorFormat());
}
Then added the HelloView Service below:
[Route("/views/hello/{Name}")]
public class HelloView
{
public string Name { get; set; }
}
public class MyServices : Service
{
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, {0}!".Fmt(request.Name) };
}
public object Any(HelloView request)
{
var razor = HostContext.GetPlugin<RazorFormat>();
var helloView = razor.GetViewPage("Hello");
var response = Any(request.ConvertTo<Hello>());
var html = razor.RenderToHtml(helloView, response);
return html;
}
}
Which works as expected where razor.GetViewPage("Hello") returns the view in Views\Hello.cshtml and it returns the generated html.
Also, is there a way to have a service gateway return the rendered razor?
The Service Gateway is used to call Services which return Typed Response DTOs, not generated html views. You can use base.Gateway to access the Service Gateway in Razor Views.

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.

Can't seem to expand an Image Navigation property with Breeze JS

I am using MVC, Entity Framework, Durandal and Breeze JS. I've got a user which looks like such (simplified):
public class User : EntityBase<Guid>, IAggregateRoot
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("UserImage")]
public virtual Guid? ImageId { get; set; }
public virtual UserImage UserImage { get; set; }
}
The UserImage class looks like such. I know I should limit the size of the Image. (Maybe this is the issue?):
public class UserImage
{
public Guid Id { get; set; }
[MaxLength]
public byte[] Image { get; set; }
}
I've got an api function on the server to get the current user:
public IQueryable<User> GetCurrentUser()
{
IPrincipal principal = HttpContext.Current.User;
var users = _uow.Users.FindBy(u => u.UserName.Equals(principal.Identity.Name));
if (!users.Any())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized));
}
return users;
}
And two calls on the client which get the current user. The first is in the shell:
function loadCurrentUser() {
return uow.CurrentUser.all().then(function (newUser) {
log('Welcome to the Site ' + newUser[0].FullName() + '!', newUser[0], true);
config.CurrentUser(newUser[0]);
return true;
});
}
The second is in a ManageUser viewmodel:
function activate() {
return uow.CurrentUser.all(['UserImage']).then(function (user) {
self.CurrentUser(user[0]);
return $.when(init()).then(boot());
}).fail(function() {
return router.activate('accounts/login');
});
}
Now I can load an Image into the ManageUser page and save and in fiddler it shows that the ImageId and Image are being sent across to the server. Then I checked the BeforeSaveEntity intercept and shows two entities being saved.
Updated User with ImageId set
New UserImage
The data is also visible in the database. Now when I refresh the Manage User page I can see the two GetCurrentUser calls in fiddler.
From the shell call I can see that the User is being returned and an ImageId is set but no UserImage was sent over because didn't expand the query.
From the Manage User call I see the User is returned but only the ImageId is sent over and the Image object was OMITTED from the JSON.
Has anyone come across this issue with images? All my other expands appear to be working correctly. Does anyone have any examples on using breeze to save just the filepath to the image and possibly using windows azure for media storage?
I know this probably won't answer your question but I would propose not sending the byte array to the client and rather have an Image Handler on the server side that takes an ImageId as a parameter and then return the image with the relevant Content Type set. An example of this can be found here.
By using this approach you could reference your image from HTML using an img tag with the source set to the Image Hander with the relevant ImageId.
An example using knockout for data binding would be:
<a data-bind="attr: {href: '/Image/' + User.ImageId()}"></a>
This approach enables you to easily add caching on both the server and client which will improve performance. It also removed the need to convert the byte array to an image on the client side, which may or may not be a pain.
Edit:
When saving the managed user, post the Image to an Upload action on the ImageHandler (have a look at this article). This action must return the new Id of the image. After you've received the new Id, update the User.ImageId on client side and call SaveChanges on breeze.

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