I am new to service stack redis api. So i am getting little confused while using the service stack redis api. I want to know IRedisTypedClient"<"T">"? 1) What stands for "<"T">"? 2) What are the parameters we can pass in the "<"T">"?
The IRedisTypeClient interface provides a typed version of the Redis Client API where all its API's accept a typed POCOs (i.e. Plain Old CSharp Object) for its value body which is in contrast to IRedisClient which just accepts raw strings. Behind the scenes the Typed API's just serialize the POCO's to a JSON string but it's typed API provides a nicer API to work with when dealing with rich complex types.
The API to create a IRedisTypeClient<T> is to use the IRedisClient.As<T> API, e.g:
public class Todo
{
public long Id { get; set; }
public string Content { get; set; }
public int Order { get; set; }
public bool Done { get; set; }
}
IRedisClient redis = redisManager.GetClient();
var redisTodos = redis.As<Todo>();
As seen above you can create a typed API from any user-defined POCO, which now provides API's that lets you work directly native Todo types, e.g:
var todo = new Todo
{
Id = redisTodos.GetNextSequence(),
Content = "Learn Redis",
Order = 1,
};
redisTodos.Store(todo);
Todo savedTodo = redisTodos.GetById(todo.Id);
savedTodo.Done = true;
redisTodos.Store(savedTodo);
"Updated Todo:".Print();
redisTodos.GetAll().ToList().PrintDump();
There's a stand-alone version of this example as well as a Live Demo of Backbones TODO app with a Redis backend which makes use of the RedisClient Typed API.
Related
I have a webapi myproj.csproj that is a .net core 3.0 web api. it compiles to a myproj client that contains all the necessary api endpoints and all types explicitly associated with those endpoints.
I have an endpoint that is like the following:
public async Task<string> GetReportDataAsync(Guid reportType, long id)
where I pass in a guid of the report type and another id, it does a calculation and returns a json string. I would like to make the type used to serialize to json on the server side available in my client so I can easily deserialize the string:
string reportOutput = await ApiClient.GetReportDataAsync(<reportTypeGuid>, <id>);
JsonConvert.DeserializeObject<MyReportType>(reportOutput);
without having to create a new API endpoint for every report type I add. Is there a way to force the web api to compile the types I ask it to for easy client consumption?
I could create a new api endpoint for every single report type, but I would rather not do that if I have 10+ report types. The existing endpoint already does what I need it to, I just want to deserialize the type.
I realize it's designed this way to have only what the client would need, just wondering if anyone has run into this.
Report Type (there are more properties than this but they are all of type long):
namespace Reports.ReportTypes
{
public class MyReportType : ReportTypeBase
{
public long JobRunId { get; set; }
public long Size { get; set; }
public long Count { get; set; }
//...
public long SomeOtherCount { get; set; }
}
}
I am using .NET Framework and ASP.NET Core to create a REST web Api.
This web api has a call that gets a request model to save data and some call that later retrieves the data.
Most of the data is structured information I need in the backend and it is saved into different fields and tables in the database. On retrieval it is loaded from those tables and returned.
This all works.
However, I now have a requirement where the caller wants to save and later retrieve arbitrary data (lets just say a random json) as one of those fields. I can save and load json from the database that is not a problem, my problem is to build the web api model for my request.
[HttpPost]
public IActionResult Save([FromBody] ApiCallRequestModel request)
{
// ...
}
public sealed class ApiCallRequestModel
{
// structured, well known information
public int? MaybeSomeNumber { get; set; }
[Required]
public string SomeText { get; set; }
[Required]
public SubModel SomeData { get; set; }
// one field of unknown json data
public ??? CustomData { get; set; }
}
I could think of dynamic or maybe even ExpandoObject or JObject to try and I might, but I would like a solution that works because it's best practice, not just because I tried and it didn't fail today with my simple tests.
If everything else fails, I could just make the field a string and tell the client to put serialized json into it. But that's a workaround I would see as a last resort if this question yields no answers.
It has proven to be extremly hard to google this topic, since all words I would use lead me to pages explaining Json serialization of my request model itself. I know how that works and it's not a problem. The mix of structured data and free json is what I cannot find out from a somewhat authorative source.
So what type would you use here, what is the best practice for receiving arbitrary json in one property of your model?
So to sum this up, as suggested I used a JToken from the Json.NET nuget package, since I already had that package in my project.
[HttpPost]
public IActionResult Save([FromBody] ApiCallRequestModel request)
{
// ...
}
public sealed class ApiCallRequestModel
{
// structured, well known information
public int? MaybeSomeNumber { get; set; }
[Required]
public string SomeText { get; set; }
[Required]
public SubModel SomeData { get; set; }
// one field of unknown json data
public JToken CustomData { get; set; }
}
Works like a charm.
How can I export a C# class (DTO) in the dtos.ts file generated with npm run typescript-ref http://localhost:5000 src/myproject without referencing in the request class?
Note: we have several C# DTO classes (MutationAddressChange, MutationCEOChange...) that we map to the domain class using automapper. So we want to use the C# DTO classes as well in Angular to populate the corresponding type (e.g.MutationAddressChangesCreateDTO) and send it to the web server. Therefore, in the CreateMutationRequest class, we accept an object instead of a specific class.
example DTO-Class:
public class MutationAddressChangesCreateDTO
{
public string Street { get; set; }
public string POBox { get; set; }
public string Zipcode { get; set; }
}
ServiceStack Request-Class
public class CreateMutationRequest : IPost
{
public object Mutation { get; set; }
}
Angular expected use:
{
var mutationAddressChangesCreateDTO= new MutationAddressChangesCreateDTO();
mutationAddressChangesCreateDTO.dateOfMutation = ...
const request = new CreateMutationRequest ({
mutation: mutationAddressChangesCreateDTO,
});
this.client.post(request)
...
}
A limitation of Add ServiceStack Reference feature is that your DTOs cannot have any object or interface properties which creates a black hole in your Services contract that's impossible to generate a Typed API for.
I'd recommend against having any object or interface properties in your DTOs which other than being a source of runtime issues is also limited by security restrictions.
You could use an untyped data structure like a Dictionary<string,string> to store arbitrary values, you can some find other alternatives in this Customer Forums thread.
Although it's discouraged you could still have object properties in your ServiceStack Request DTOs, you just wont be able to generate a typed API for them but you should still be able to send them as an anonymous arg, e.g:
this.client.post(request, { mutation: dto });
Object properties are handled with JS Utils by default which should deserialize it into a Dictionary<string,object> which you should be able to convert back into a C# type using ServiceStack's Reflection Utils, e.g:
public object Any(CreateMutationRequest request)
{
var payload = request.Mutation as Dictionary<string,object>;
var payloadRequest = payload.FromObjectDictionary(typeof(TheType));
}
A similar approach to this that avoids using object is to send a serialized JSON payload in a string property, e.g:
request.mutation = JSON.stringify(payload);
Which you can deserialize using JS Utils again, e.g:
public object Any(CreateMutationRequest request)
{
var payload = JSON.parse(request.Mutation);
var payloadRequest = payload.FromObjectDictionary(typeof(TheType));
}
With that said I don't recommend any of these untyped strategies and would personally create Typed services for each API that's needed which is more intuitive, discoverable & resilient, any shared functionality can easily be handled in your Services implementation using ServiceStack's AutoMapping and .NET's powerful reflection capabilities.
I have a question on the implementation of android -> JSON -> RESTful WCF webserive architecture. I'm primarily coming from a C# pattern based background so finding the android architecture hard to get my head around. The way I have designed my C# webservice is to have a whole "model" namespace that defines all my business objects such as User, UserProfile, UserSearchCriteria, UserPhoto etc as CLR objects.
I have exposed my webservice interface through WCF using the RESTful model, combined with request/response pattern which I am used to. One example of a REST web method is as follows.
[OperationContract]
[WebInvoke(
Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/GetSeeds"
)]
GetSeedsResponse GetSeeds(GetSeedsRequest request);
Response object
[DataContract]
public class GetSeedsResponse
{
public GetSeedsResponse(List<Seed> seeds)
{
Seeds = seeds;
}
[DataMember]
public List<Seed> Seeds { get; set; }
}
Request object
[DataContract]
public class GetSeedsRequest : BaseRequest
{
public GetSeedsRequest(int userId, int numberToTake, int startPosition)
{
NumberToTake = numberToTake;
StartPosition = startPosition;
UserId = userId;
}
[DataMember]
public int NumberToTake { get; set; }
[DataMember]
public int StartPosition { get; set; }
[DataMember]
public int UserId { get; set; }
}
Note: all my request objects inherit from my own custom base request object which merely provides them all a common header for authentication, as follows (I am not sure if this is the best way to handle authentication but it is what I am familiar with so have done it this way for now)
[DataContract]
public abstract class BaseRequest
{
[DataMember]
public Header Header { get; set; }
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.2022")]
[System.SerializableAttribute]
[System.Diagnostics.DebuggerStepThroughAttribute]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:MyStory:Web:Services:ServiceModel:Headers")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="urn:MyStory:Web:Services:ServiceModel:Headers", IsNullable=true)]
public class Header
{
public int UserId { get; set; }
public string SessionToken { get; set; }
}
So far I have managed to successfully call the RESTful webmethod from android with the following code in an AsyncTask background worker.
protected ArrayList<Seed> doInBackground(String... params)
{
try
{
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(SERVICE_URI);
post.setHeader("Accept", "application/json");
post.setHeader("Content-type", "application/json");
JSONObject data = new JSONObject();
data.put("UserId", 1);
data.put("NumberToTake", 10);
data.put("StartPosition", 0);
StringEntity entity = new StringEntity(data.toString());
post.setEntity(entity);
HttpResponse response = client.execute(post);
}
...
}
I would then seek to extract the response and manually construct a "Seed" object based on an android class I would define, matching the properties of the CLR version, and manually extracting from the JSON response the indexed fields for which I would know which one corresponded to which property.
My question is in two parts
1) I cannot figure out how to pass the header object. I have tried the following
JSONObject header = new JSONObject();
header.put("UserId", 1);
header.put("SessionToken", "");
JSONObject data = new JSONObject();
data.put("UserId", 1);
data.put("NumberToTake", 10);
data.put("StartPosition", 0);
data.put("Header", header);
But this fails to work with a generic "Bad request error" exception and the webservice doesn't even enter the webmethod (I have set a break point on line 1 which gets hit without the header, but doesn't get hit when I include the header), so I assume I am not understanding JSON properly or the way to pass an embedded object in the Request object
2) Am I doing the whole architectural approach correct? It seems quite laborious and prone to error to be manually constructing array indexed requests and responses for every web service call. How should I be structuring my project, creating matching android objects for all CLR objects my webservice wants to accept as parameters or return as response objects? Is there a lot more power to JSON that I am not aware of that I should be using? With the requests, do I need to create bespoke individual AsyncTask objects for every single web service call I might make? There are about 40 different web methods my app will call, does this mean 40 AsyncTask objects kept in a package somewhere in my codebase? I'm struggling to figure out the "best practices" for designing a decent sized app with decent amounts of web service interaction, if anyone has any books they could reccomend or tutorials that touch on best practices for android project design, architecture for my scenario then I would be very grateful.
Apologies for the length of the post
Decouple the Android side from the Windows server side. The reason we use JSON and REST is that they are system agnostic. Don't worry about the structure on the Windows side. Instead focus on what JSON request will work for the server, and focus on creating that JSON on Android.
First, get a REST client like POSTMan and figure out what the REST call looks like, and what the JSON body looks like.
Then, figure out how to make Android send that body.
Finally, figure out how to make Android generate the JSON body programatically. For this last one, I strongly recommend Gson. It will let you work with higher level objects instead of constructing your JSON manually. If you update your request with the JSON body I'll update this response with how it could be generated in Gson.
As a side note, I recommend you look at OkHttp instead of the Apache HTTP client. It's much more full featured and robust. This isn't necessary at all, but it will make your life easier.
I am attempting to get ServiceStack to return a list of objects to a C# client, but I keep getting this exception:
"... System.Runtime.Serialization.SerializationException: Type definitions should start with a '{' ...."
The model I am trying to return:
public class ServiceCallModel
{
public ServiceCallModel()
{
call_uid = 0;
}
public ServiceCallModel(int callUid)
{
this.call_uid = callUid;
}
public int call_uid { get; set; }
public int store_uid { get; set; }
...... <many more properties> ......
public bool cap_expense { get; set; }
public bool is_new { get; set; }
// An array of properties to exclude from property building
public string[] excludedProperties = { "" };
}
The response:
public class ServiceCallResponse
{
public List<ServiceCallModel> Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
And the service:
public class ServiceCallsService : Service
{
// An instance of model factory
ModelFactory MyModelFactory = new ModelFactory();
public object Any(ServiceCallModel request)
{
if (request.call_uid != 0)
{
return MyModelFactory.GetServiceCalls(request.call_uid);
} else {
return MyModelFactory.GetServiceCalls() ;
}
}
}
The client accesses the service with:
JsonServiceClient client = new ServiceStack.ServiceClient.Web.JsonServiceClient("http://172.16.0.15/");
client.SetCredentials("user", "1234");
client.AlwaysSendBasicAuthHeader = true;
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
The "model factory" class is a DB access class which returns a list. Everything seems to work just fine when I access the service through a web browser. The JSON returned from the service starts:
"[{"call_uid":70...."
And ends with:
"....false,"is_new":true}]"
My question is, what here might be causing serialization/deserialization to fail?
Solution
Thanks to the answer from mythz, I was able to figure out what I was doing wrong. My misunderstanding was in exactly how many DTO types there are and exactly what they do. In my mind I had them sort of merged together in some incorrect way. So now as I understand it:
Object to return (In my case, called "ServiceCallModel": The actual class you wish the client to have once ServiceStack has done its job. In my case, a ServiceCallModel is a key class in my program which many other classes consume and create.
Request DTO: This is what the client sends to the server and contains anything related to making a request. Variables, etc.
Response DTO: The response that the server sends back to the requesting client. This contains a single data object (ServiceCallModel), or in my case... a list of ServiceCallModel.
Further, exactly as Mythz said, I now understand the reason for adding "IReturn" to the request DTO is so the client will know precisely what the server will send back to it. In my case I am using the list of ServiceCallModel as the data source for a ListView in Android. So its nice to be able to tell a ListViewAdapter that "response.Result" is in fact already a useful list.
Thanks Mythz for your help.
This error:
Type definitions should start with a '{'
Happens when the shape of the JSON doesn't match what it's expecting, which for this example:
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
The client is expecting the Service to return a ServiceCallResponse, but it's not clear from the info provided that this is happening - though the error is suggesting it's not.
Add Type Safety
Although it doesn't change the behavior, if you specify types in your services you can assert that it returns the expected type, e.g Change object to ServiceCallResponse, e.g:
public ServiceCallResponse Any(ServiceCallModel request)
{
...
}
To save clients guessing what a service returns, you can just specify it on the Request DTO with:
public class ServiceCallModel : IReturn<ServiceCallResponse>
{
...
}
This lets your clients have a more succinct and typed API, e.g:
ServiceCallResponse response = client.Get(new ServiceCallModel());
instead of:
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
See the New API and C# Clients docs for more info.