Here: Recommended ServiceStack API Structure and here: https://github.com/ServiceStack/ServiceStack/wiki/Physical-project-structure are recommendations for how to structure your projects for C# clients to reuse DTOs.
Apparently this is done by including a dll of the DTO assembly. I have searched the web for one example, just Hello World that uses a separate assembly DTO for a C# client in ServiceStack. Perhaps I should be able to break this out myself but so far it has not proven that easy.
Almost all client descriptions are for generic and non-typed JSON or other non-DTO based clients. No one appears interested in typed C# clients like I am (even the ServiceStack documentation I have found). So I thought this would be a good question even if I figure it out myself in the end.
To be clear, I have built and run the Hello World example server. I have also used a browser to attach to the server and interact with it. I have also created a client empty project that can call
JsonServiceClient client = new JsonServiceClient(myURL);
Then I tried to copy over my DTO definition without the assembly DLL as I don't have one. I get ResponseStatus is undefined.
Clearly there is something missing (it appears to be defined in ServiceStack.Interfaces.dll) and if I could create a dll of the DTO I think it would resolve all references.
Can anyone give insight into how to create the DTO assembly for the simple Hello World?
Edited to add code:
using ServiceStack.ServiceClient.Web;
namespace TestServiceStack
{
class HelloClient
{ public class HelloResponse
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
//Request DTO
public class Hello
{
public string Name { get; set; }
}
HelloResponse response = client.Get(new Hello { Name = "World!" });
}
}
Where the ResponceStatus is undefined.
I was able to find the missing symbol ResponseStatus by adding:
using ServiceStack.ServiceInterface.ServiceModel;
Here is the full code that built. Keep in mind that I found out something else in the process. Once this built it then failed because I was using a DTO from a .NET 4.0 environment in a .NET 3.5 environment. But that is an unrelated issue. Also note that this test code does nothing with the response, it is just an example to get the build working.
using ServiceStack.ServiceClient;
using ServiceStack.ServiceInterface;
using ServiceStack.Text;
using ServiceStack.Service;
using ServiceStack.ServiceHost;
using ServiceStack.WebHost;
using ServiceStack;
using ServiceStack.ServiceClient.Web;
using RestTestRoot; // This is the name of my DTO assembly. You will need to insert your own here.
using ServiceStack.ServiceInterface.ServiceModel;
namespace WebApplicationRoot
{
class HelloClient
{
JsonServiceClient hello_client;
//Request DTO
public class Hello
{
public string Name { get; set; }
}
//Response DTO
public class HelloResponse
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
//Can be called via any endpoint or format, see: http://mono.servicestack.net/ServiceStack.Hello/
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
//REST Resource DTO
[Route("/todos")]
[Route("/todos/{Ids}")]
public class Todos : IReturn<List<Todo>>
{
public long[] Ids { get; set; }
public Todos(params long[] ids)
{
this.Ids = ids;
}
}
[Route("/todos", "POST")]
[Route("/todos/{Id}", "PUT")]
public class Todo : IReturn<Todo>
{
public long Id { get; set; }
public string Content { get; set; }
public int Order { get; set; }
public bool Done { get; set; }
}
public HelloClient(){
// ServiceStack gateway = new ServiceStack.ClientGateway(
// location.protocol + "//" + location.host + '/ServiceStack.Examples.Host.Web/ServiceStack/');
hello_client = new JsonServiceClient("http://tradetree2.dnsapi.info:8080/");
hello_client.Get<HelloResponse>("/hello/MyTestWorld!");
}
}
}
Related
I might not have worded this question in the correct format as it's difficult to explain what the problem is. From the research I've gathered I think database hierarchy is something that I'm trying to utilize when using Entity Framework to create a database, I'll try to explain my problem in the best way possible.
Firstly, I'm running a Visual Studio 2019 project with a console application. This is my Program.cs file which is currently using a get request from an API URL with RESTSHARP then performing deserialization.
static void Main(string[] args)
{
getAPIData()
}
public static void getAPIData()
{
var client = new RestClient(URL);
var request = new RestRequest();
var response = client.Execute(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string rawResponse = response.Content;
Requests.Rootobject result = JsonConvert.DeserializeObject<Requests.Rootobject>(rawResponse);
}
}
As you can see above, the raw data that's gathered from the URL (my API) is converted to a JSON format. And when I debug the code, it stores the code perfectly into the Requests DTO, as I'll show below.
public class Requests
{
public int Id { get; set; }
public class Rootobject
{
public Operation operation { get; set; }
}
public class Operation
{
public Result result { get; set; }
public Detail[] details { get; set; }
}
public class Result
{
public string message { get; set; }
public string status { get; set; }
}
public class Detail
{
public string requester { get; set; }
public string workorderid { get; set; }
public string accountname { get; set; }
}
}
The recently converted API data is now stored in this class in this correct format. Which is what's necessary for the API itself since it comes in numerous classes. This object now holds all the relevant data I need, now I can utilize the entity framework to generate an appropriate table that will hold this into an SQL server.
Here is the Entity Framework class.
public class TransitionContext : DbContext
{
private const string connectionString = #"(connection string here)";
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(connectionString);
}
public DbSet<Requests> Requesting { get; set; }
}
Currently, when I run the package manager console commands to build a migration and setup a connection to the server, it works fine (Obviously I've not included my real connection string for privacy reasons). However, when I use the "Update-Database" command from the DbSet which is my DTO for the API data. The only key that is displayed is an ID, nothing else.
SQL requests table
All that's generated here is an Id key from the Requests Dto. Now, on the API itself, there is no ID key, I added it in there because Entity Framework said I had to have an ID key and was displaying errors until I made one. But now that's all it's generating when I "Update-Database".
The reason why I showed as much data as I have done, is because the process is simply exporting the converted JSON data (a recently gathered API) into an SQL table. Or tables so that it supports a hierarchy, anything that will map correctly from the DTO to the database. But as of right now, my only issue with the project is that only an ID is being generated.
So I'm not sure where I'm going wrong here or how to get this working as I intend it to work. Apologies for the really long explanation, I tried to condense it as much as I could, any help would be appreciated and any questions I'm free to answer, thank you!
So I am trying to write a server with a database which would host a site similar to reddit: with users, groups, posts and comments.
As this is my first time developing backend, I tried googling and found ASP.NET Core 3.1 Web API with Entity Framework to be "good". (I use SQL Server as the database server if that's relevant)
Now I went with the code first method, and created the tables with a migration.
(ERD)
I used the built in tool to create the controllers.
If I used this JSON to create a post entity (with HTTP POST), the AuthorId and the LocationId stay on NULL (weather I use "" for the numbers or not). I created the Group and the User it would refer to earlier.
The JSON:
{
"title": "Post",
"posttext": "text",
"creatorid": "1",
"locationid": "1",
"timeofpost": "2020-01-12"
}
The Post class:
using System;
using System.Collections.Generic;
namespace CedditBackend.Models
{
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
#nullable enable
public string? PostText { get; set; }
public byte[]? Content { get; set; }
public ICollection<Comment>? Comments { get; set; }
#nullable disable
public User Creator { get; set; }
public Group Location { get; set; }
public DateTime TimeOfPost { get; set; }
}
}
The autogenerated HTTPPOST in the controller:
[HttpPost]
public async Task<ActionResult<Post>> PostPost(Post post)
{
_context.Posts.Add(post);
await _context.SaveChangesAsync();
return CreatedAtAction("GetPost", new { id = post.Id }, post);
}
On the other hand if I try and create my own post class from the JSON (basically deserializing it), I get an error that "a cycle was detected". This I tried googling, but only found that with an earlier version (I guess) I could just turn that off.
Still if I open the database, it shows me that the values of LocationId and AuthorId are not NULL, but if I try to get the posts of the Users (with an HTTP GET), all I get is the same error message.
My rewritten HTTPPOST
[HttpPost]
public async Task<ActionResult<Post>> PostPost(Object obj)
{
Dictionary<string, object> dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(obj.ToString());
Post post = new Post();
post.Title = dict["title"].ToString();
post.PostText = dict["posttext"].ToString();
post.TimeOfPost = DateTime.Parse(dict["timeofpost"].ToString());
post.Location = _context.Groups.FirstOrDefault(x => x.Id == int.Parse(dict["locationid"].ToString()));
post.Creator = _context.Users.FirstOrDefault(x => x.Id == int.Parse(dict["creatorid"].ToString()));
post.Creator.Posts.Add(post);
post.Location.Posts.Add(post);
_context.Posts.Add(post);
await _context.SaveChangesAsync();
return CreatedAtAction("GetPost", new { id = post.Id }, post);
}
I tried to google this whole stuff in several ways, but found nothing. Am I missing something obvious?
Edit: the User class (which I think causes the cycle reference)
using System.Collections.Generic;
namespace CedditBackend.Models
{
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
#nullable enable
public ICollection<Post>? Posts { get; set; } = new List<Post>();
public ICollection<Comment>? Comments { get; set; } = new List<Comment>();
public ICollection<UserGroup>? UserGroups { get; set; }
#nullable disable
}
}
It looks like you're running into the same problem I currently have.
The new JSON library System.Text.JSON is currently unable to handle One-to-Many relationships it seems. They are currently tracking the issue here, but a fix isn't planned until .NET 5. I haven't found a solution at this point but I will update this answer if I do.
UPDATE
Found a workaround, at least for now.
Install Newtonsoft MVC Extensions
PM> Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.1
Edit ConfigureServices method in Startup.cs
services
.AddControllers()
.AddNewtonsoftJson()
Now configure the loop handling in Newtonsoft
services
.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
That should be enough to get it working. Keep in mind if you have any custom JsonConverters you will have to change to the converters provided in the Newtonsoft library to get them to work.
Try removing these lines.
post.Creator.Posts.Add(post);
post.Location.Posts.Add(post);
You've already defined the Creator and Location of your post. Adding it to their collections would make it a circular reference.
I am using entity framwork in my Wep Api project. My model is realational and line this:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
public class Article
{
public int Id { get; set; }
public int BlogId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public virtual Blog Blog { get; set; }
}
context is like this:
public class BloggingContext: DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Article> Articles { get; set; }
}
And I am using this in my asp.net Web Api controller.
public class BlogController: ApiController{
public IEnumerable<Blog> Get(){
var context = new BloggingContext();
return context.Blogs.ToList();
}
}
My approach is to get data with Lazy Loading, and serialize to JSON data as Web Api response.
context.Blogs.ToList() returns the relational data (I see on breakpoint).
But Web Api result has error.
Exception Message:
The 'ObjectContent`1' type failed to serialize the response body for
content type 'application/json; charset=utf-8
inner exception:
Error getting value from 'Blog' on
'System.Data.Entity.DynamicProxies.Article_D002A1ECE031410435306DCEF780AFF03EBB8BD36DA603662C993107FAEB1917 "ExceptionType": "Newtonsoft.Json.JsonSerializationException
I set my WebApiConfig.cs
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
jsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
You might try disabling proxy generation. At the minimum, that may give you a clearer exception if it doesn't fix the issue.
context.Configuration.ProxyCreationEnabled = false;
The old lazy loading questions! Such fun!
So firstly, when you breakpoint and evaluate an entity this by itself triggers lazy loading. Entity Framework is clever like that, but not helpful. When in fact, if you are not debugging, those same properties will be null.
You have 2 options here, 1. Eager Load or 2. As previously mentioned, use a transfer object. But why will they will.
is obvious. by using context.Blogs.Include(x => x... etc) you are telling entity framework just to go ahead and load them because we will need it.
Why does using a transfer object work? Well in order to transfer the data into the new object you must call the Get method on all properties within the old entity, thus triggering lazy loading. For this you can use 3rd party packages such as AutoMapper but they can cause overhead.
For an API I personally suggest Eager loading. The point of an API is that you as the develop design endpoints and you restrict what can and can't be used. If you want lazy loading then I suggest MVC is a better option.
Try using data transfer objects in your controller.In my case this fixed the issue.BlogModel is the name of the new class that you have to add to use as DTO.It has the same properties as your entity class.
public IEnumerable<Blog> Get(){
List<BlogModel> listofModels = new List<BlogModel>();
foreach(var model in whateveristhenameofyourentity.Blogs)
{
BlogModel blogModel = new BlogModel();
blogModel.name = model.name;
.
.
.
listofModels.Add(BlogModel);
}
IEnumerable<BlogModel> models = listofModels.AsIEnumerable();
return models;
}
I am working with an API that returns data in JSON format (as far as I can tell, this is my first time working with a true API or JSON). I read a bunch about working with JSON in C#, and eventually got the Newtonsoft.Json library. Unfortunately, I am having a hard time converting the response I am receiving into a C# class following the examples that exist in the Newtonsoft documentation.
Here is an example of the data returned by this API:
{"name":{"id":1,"name":"name","pID":1,"revisionDate":1390580000000}}
And heres what I have so far:
public class apiDataObject
{
public long id {get; set;}
public string name { get; set; }
public int pID { get; set; }
public long revisionDate { get; set; }
}
public long getID()
{
try
{
data = WebRequest.Create(baseURL);
retData = data.GetResponse().GetResponseStream();
}
catch (Exception exception)
{
outputBox.AppendText(Environment.NewLine + exception.ToString());
}
retDataReader = new StreamReader(retData);
returnedData = retDataReader.ReadToEnd();
outputBox.AppendText(returnedData);
apiDataObject test = new apiDataObject();
JsonConvert.PopulateObject(returnedData, test);
return test.id;
}
I have also tried replacing the JsonConvert.PopulateObject(returnedData, test) with:
apiDataObject test = JsonConvert.DeserializeObject<apiDataObject>(returnedData)
The problem is that my "test" object is always empty after the code finishes. I have stepped through the code, and everything works great until I get to the lines where the test object is created, and supposedly populated. I also tried the inbuilt Microsoft libraries and had the exact same issue. I am honestly stumped, I have spent 2 or 3 hours looking at these few lines of code and tons of documentation and samples of the Newtonsoft.Json library, but simply cant figure out where I've gone wrong here. Any help would be greatly appreciated.
From the JSON you posted, its actually a dictionary type: I changed your method to show you, I tested it out and it works.
public long GetID()
{
var testDict = new Dictionary<string, apiDataObject>();
var returnedData = "{\"name\":{\"id\":1,\"name\":\"name\",\"pID\":1,\"revisionDate\":1390580000000}}";
JsonConvert.PopulateObject(returnedData, testDict);
return testDict["name"].id;
}
Running your original code throws an exception telling you that it doesn't know what to do with the first "name".
Just in case anyone ever comes across this in a search, I figured out an alternative solution to working with this type of data as well. The Newtonsoft.Json library contains a function called DeserializeObject. So for the sample data of:
{"name":{"id":1,"name":"name","pID":1,"revisionDate":1390580000000}}
You can create an object that looks like:
public class Name
{
public int id { get; set; }
public string name { get; set; }
public int pID { get; set; }
public long revisionDate { get; set; }
}
public class RootObject
{
public Name name { get; set; }
}
and then use:
JsonConvert.DeserializeObject<RootObject>(returnedData);
to convert the json into the object without having to use a dictionary.
This is probably "common knowledge", considering the object code can easily be created using the json2csharp converter someone linked earlier, but I was unable to find any direct explanation about when to use the DeserializeObject function or why it should be used versus PopulateObject.
Environment is Visual Studio 2012, ServiceStack, ASP.NET Web Application Project (followed https://github.com/ServiceStack/ServiceStack/wiki/Create-your-first-webservice)
Looking through some of the classes in ServiceStack.Examples, I noticed that most of the services contain only one method. Either some override on Execute() or, if a REST service, some override of OnPost/Get/Put/Delete().
How should I approach making a full API set, if I have tens of functions I need implemented RegisterUser(), RemoveUser(), AddFriend(), RemoveFriend() ... One service per method?
public RegisterUserService : IService<User> { public object Execute(User> dto) { ... } }
public RemoveUserService : IService<User> { public object Execute(User> dto) { ... } }
public AddFriendService : IService<Friend> { public object Execute(Friend dto) { ... } }
public RemoveFriendService: IService<RequestDTO4> { public object Execute(Friend dto) { ... } }
I'm pretty lost on how to begin implementing a full API set. I've read the first and second wiki page on 'Creating your first webservice', which I've copied to make 1 service method. But now I want to make 10 or 40 service methods and I'm not sure how to do that.
I noticed that implementing from IRestService<T> allows you up to 4 methods instead of the one Execute() method, simply because each method corresponds to a different HTTP verb. So is there something like that I could write? Basically something like:
public MyService : IService/IRestService/ServiceBase?<User>
{
public object AddUser(User user) { }
public object RemoveUser(User user) { }
public object ModifyUser(User user) { }
}
Just looking for something that doesn't necessarily have to contain all methods in one service class, but as many as reasonably possible ... do I really have to create 1 service for each service method?
Note on pursuing a strictly RESTful architecture: I only read up a little on REST, but it seems like I'd have to strictly follow rules like: treat everything as a resource even if you have to re-design your models, no verbs in the URL names (/Friends, not /GetFriends because REST gives you OnGet(), OnPost(), OnPut(), and OnDelete() ... basically I'm interested in the easiest, quickest, and most painless way of implementing a a few dozen service methods. It's a personal project, so the requirements won't vary too much.
Thanks in advance for guiding me through this first step.
EDIT: Just saw this related question: How to send commands using ServiceStack?
Mythz said there's no "ServiceStack way" to design. The guy's question is pretty much like mine. I'm wondering how to stack a lot of service methods in a service.
EDIT 2: Just saw Need help on servicestack implementation, and Separate or combined ServiceStack services?.
I just tested the code below successfully with working routes:
[Route("/registerUser/setEmail/{Email}")]
[Route("/registerUser/setPassword/{Password}")]
[Route("/registerUser/setPhoneNumber/{PhoneNumber}")]
[Route("/lalal2395823")]
[Route("/test3234/test23423511")]
public class RegisterUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Nickname { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
But what I'd like is for each [Route("path")] to go to a different method, instead of having them all parsed in Execute() and having to parse which string isn't null or empty.
My Solution
I decided to take Rickard's advice and make a proper REST API, because it seems simpler and cleaner in the end.
This is now my class using the new ServiceStack API (new as of 9/24/12):
using UserModel = Project.Model.Entities.User;
[Route("/User", "POST")]
[Route("/User/{FirstName}", "POST")]
[Route("/User/{FirstName}/{LastName}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}/{PhoneNumber}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}/{PhoneNumber}/{Email}", "POST")]
public class CreateUser : IReturn<UserModel>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Nickname { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
public class CreateUserService : Service
{
public object Post(CreateUser request)
{
try
{
using (var session = FluentNHibernateHelper.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var user = new UserModel()
{
FirstName = request.FirstName,
LastName = request.LastName,
Nickname = request.Nickname,
PhoneNumber = request.PhoneNumber,
Email = request.Email,
Password = request.Password,
};
session.SaveOrUpdate(user);
transaction.Commit();
return user;
}
}
}
catch
{
throw;
}
}
}
This is now a lot simpler with ServiceStack's new API Design released in (v3.9.15+).
#Rickard makes a lot of good points on how to re-structure your service so it's more REST-ful which is now easier to achieve with ServiceStack's new API which is now less restrictive and more flexible where the same service can handle multiple different Request DTOs and you're no longer restricted in the Response Type you can return.
Following the HTTP way you have to turn your way of thinking upside down. You need to think in terms of resources, i.e. users, friends, etc. Using HTTP you already have a finite set of methods, namely Get, Put, Post, and Delete.
Hence, the service API design could look like this:
RegisterUser() => POST /users
RemoveUser() => DELETE /users/{userid}
AddFriend() => POST /users/{userid}/friends
RemoveFriend() => DELETE /users/{userid}/friends/{friendid}
ModifyUser() => PUT /users/{userid}
etc.
To implement for example RemoveFriend in ServiceStack you could do like this:
public class UserFriendService : RestServiceBase<UserFriendRequest>
{
public override object OnPost(UserFriendRequest request)
{
// pseudo code
var user = GetUser(request.UserId);
var friend = GetUser(request.FriendId); // FriendId is a field in the HTTP body
user.Friends.Add(friend);
return HttpResult.Status201Created(user, ...);
}
//...
}
[Route("/users/{userId}/friends")]
public class UserFriendRequest
{
public string UserId { get; set; }
public string FriendId { get; set; }
}