The question title might be a bit difficult to understand, but I'll try to explain what I am asking here.
I have this sort of controller method in an ASP.NET MVC controller:
public IActionResult DoStuff([FromBody] RequestObject reqObj)
{
// do stuff
ReplyObject repObj = ProcessObject(reqObj);
// do more stuff
return Json(repObj);
}
where RequestObject looks like this:
public class RequestObject
{
public string Field { get; set; }
}
I am calling it from an Angular service like this:
this.http.post("/StuffController/DoStuff", { field: "field value goes here" })
Now this works OK. But now I need DoStuff() to handle different subclasses of RequestObj, for example:
public class RequestObjectA : RequestObject
{
public string FieldA { get; set; }
}
public class RequestObjectB : RequestObject
{
public string FieldB { get; set; }
}
and have DoStuff() somehow figure out whether it was given a RequestObjectA or a RequestObjectB.
Is this kind of thing possible? I have a feeling it is not as the JSON request transmits only the contents of the object, not its class, but I'd like to know for sure.
The short answer is that it is not possible.
The long answer is: your contract for the endpoint parameter is RequestObject class so nor additional parameter members will be respected during deserialization as nothing is known about them.
I'm not sure if this is possible or not.
I have a method call from client api to service api (two separate projects in two separate locations) that I want post sharp to intercept. Calls are from the client and postsharp is on the service
service.GetLogin(username)
The key here is I need to pass an authorization object that will show in the postsharp onentry method
public class Authorization
{
public string client_id { get; set; }
public string client_secret { get; set; }
public string access_token { get; set; }
public string token_type { get; set; }
public string expires_in { get; set; }
}
var auth = new Authorization();
auth.client_id = "xyz";
auth.client_secret = "abc"
and in postsharp
[Serializable]
public class LoggingAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
//I need the object to show up here
}
}
Problem:
I do not want to pass this object in every single method call as I have over 1000 methods. Is there a way to attach that authorization object to each call so that postsharp can see it without doing something like this
service.GetLogin(username, auth);
service.Foo(auth)
service.Bar(auth);
service.xyz(auth);
services.abc(auth);
Can you imagine adding just this one object to over 1000 methods?
PostSharp is unable to directly consume data from call sites, as it would require adding an actual parameter to the method (or emitting a new overload). This is in theory possible with the low-level SDK, but would be hard to maintain and costly to implement.
The best way to achieve this is to use AsyncLocal<T> which allows you to store data in the ExecutionContext.
public class AuthorizationProvider
{
private static AsyncLocal<Authorization> state = new AsyncLocal<Authorization>();
public Authorization Current { get => state.Value; set => state.Value = value; }
}
Code, where authorization is valid, will be:
Authorization authorization = ... ;
try
{
AuthorizationProvider.Current = authorization;
// your logic.
}
finally
{
AuthorizationProvider.Current = null;
}
Then you can read the authorization object from OnEntry:
[Serializable]
public class LoggingAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
if (!(AuthorizationProvider.Current?.IsValid ?? false))
{
...
}
}
}
You can also wrap the setting/unsetting of the current Authorization in an IDisposable so that you can use using statement.
Values set assigned in the AsyncLocal are stored in the execution context and as such flow through async calls. The value stored in the execution context which may be captured and used after you remove the value in the finally block above. See more here.
I have always wondered what the best practice is for separating code in a class based language. As an example, I made a project that handles api interaction with my web api. I want to know what the right option is to go with, or another suggestion.
Example 1
Project Files
Api.cs
DataTypes
Anime.cs
Episode.cs
Api.cs
public class Api
{
public static async Task<List<Anime>> GetAnimesByKeyword(string keyword)
{
// Execute api request to server
return result;
}
public static async Task<List<Episode>> GetEpisodesByAnime(Anime anime)
{
// Execute api request to server
return result;
}
}
DataTypes -> Anime.cs
public class Anime
{
public string Name { get; set; }
public string Summary { get; set; }
// Other properties
}
DataTypes -> Episode.cs
public class Episode
{
public string Name { get; set; }
public Date ReleaseDate { get; set; }
// Other properties
}
Or example 2
Project Files
Api.cs
DataTypes
Anime.cs
Episode.cs
Api.cs
public class Api
{
// Nothing for now
}
DataTypes -> Anime.cs
public class Anime
{
public static async Task<Anime> GetById(int id)
{
return result;
}
public string Name { get; set; }
public string Summary { get; set; }
// Other properties
}
DataTypes -> Episode.cs
public class Episode
{
public static async Task<List<Episode>> GetEpisodesByAnime(Anime anime)
{
return result;
}
public string Name { get; set; }
public Date ReleaseDate { get; set; }
// Other properties
}
What of these 2 is the preferred way of structuring the code, or is there a better way to do this. It might seem insignificant, but it does matter to me.
Thanks for helping me out!
In general, follow the Single Responsibility Principle. In practice this means you have simple objects that are data-only and more complex service classes that do work like loading from an external service or database.
Your second example mixes concerns AND it binds these two classes together tightly (Episode now depends on Anime). You can also see how it's hard to decide which class to put that loading method on: should it be anime.GetEpisodes() or Episode.GetEpisodesByAnime()? As the object graph gets more complex this escalates.
Later you may well want a different data transfer object for an entity. Having simple data-only objects makes it easy to add these and to use Automapper to convert.
But (on your first example) don't use static methods because that makes your service class harder to test. One service may depend on another (use dependency injection) and to test each in isolation you don't want to have static methods.
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!");
}
}
}
<TL;DR>
At a minimum, I'm looking for a way to conditionally exclude certain properties on the resource from being included in the response on a per-call basis (See fields below).
Ideally, I'd like to implement a REST service with ServiceStack that supports all the major points below.
UPDATE
While I really like ServiceStack's approach in general and would prefer to use it if possible, if it isn't particularly well suited towards these ideas I'd rather not bend over backwards bastardizing it to make it work. If that's the case, can anyone point to another c# framework that might be more appropriate? I'm actively exploring other options myself, of course.
</TD;DR>
In this talk entitled Designing REST + JSON APIs, the presenter describes his strategy for Resource References (via href property on resources) in JSON. In addition to this, he describes two query parameters (fields and expand) for controlling what data is included the response of a call to a REST service. I've been trying without success to dig into the ServiceStack framework to achieve support for fields in particular but have thus far been unsuccessful. Is this currently possible in ServiceStack? Ideally the solution would be format agnostic and would therefore work across all of ServiceStack's supported output formats. I would imagine expand would follow the same strategy.
I'll describe these features here but I think the talk at the link does a better job of explaining them.
Lets say we have an Profiles resource with the following properties: givenName, surname, gender, and favColor. The Profiles resource also includes a list of social networks the user belongs to in the socialNetworks property.
href - (42:22 in video) Every resource includes a full link to it on the REST service. A call to GET /profiles/123 would return
{
"href":"https://host/profiles/123",
"givenName":"Bob",
"surname":"Smith",
"gender":"male",
"favColor":"red",
"socialNetworks": {
"href":"https://host/socialNetworkMemberships?profileId=123"
}
}
Notice that the socialNetworks property returns an object with just the href value populated. This keeps the response short and focused while also giving the end user enough information to make further requests if desired. The href property, used across the board in this manor, makes it easy (conceptually anyway) to reuse resource data structures as children of other resources.
fields - (55:44 in video) Query string parameter that instructs the server to only include the specified properties of the desired resource in the REST response.
A normal response from GET /profiles/123 would include all the properties of the resource as seen above. When the fields query param is included in the request, only the fields specified are returned. 'GET /propfiles/123?fields=surname,favColor' would return
{
"href":"https://host/profiles/123",
"surname":"Smith",
"favColor":"red"
}
expand - (45:53 in video) Query string parameter that instructs the server to flesh out the specified child resources in the result. Using our example, if you were to call GET /profiles/123?expand=socialNetworks you might receive something like
{
"href":"https://host/profiles/123",
"givenName":"Bob",
"surname":"Smith",
"gender":"male",
"favColor":"red",
"socialNetworks": {
"href":"https://host/socialNetworkMemberships?profileId=123",
"items": [
{
"href":"https://host/socialNetworkMemberships/abcde",
"siteName":"Facebook",
"profileUrl":"http://www.facebook.com/..."
},
...
]
}
}
So...in my opinion ServiceStack's best feature is that it makes sending, receiving and handling POCOs over HTTP super easy. How you set up the POCOs and what you do in between (within the 'Service') is up to you. Does SS have opinions? Yes. Do you have to agree with them? No. (But you probably should :))
I think expanding on something like below would get you close to how you want to handle your api. Probably not the best example of ServiceStack but the ServiceStack code/requirements are barely noticeable and don't get in your way (AppHost configure not shown). You could probably do something similar in other .NET Frameworks (MVC/Web API/etc) but, in my opinion, won't look as much like straight C#/.NET code as with ServiceStack.
Request classes
[Route("/Profiles/{Id}")]
public class Profiles
{
public int? Id { get; set; }
}
[Route("/SocialNetworks/{Id}")]
public class SocialNetworks
{
public int? Id { get; set; }
}
Base Response class
public class BaseResponse
{
protected virtual string hrefPath
{
get { return ""; }
}
public string Id { get; set; }
public string href { get { return hrefPath + Id; } }
}
Classes from example
public class Profile : BaseResponse
{
protected override string hrefPath { get { return "https://host/profiles/"; } }
public string GivenName { get; set; }
public string SurName { get; set; }
public string Gender { get; set; }
public string FavColor { get; set; }
public List<BaseResponse> SocialNetworks { get; set; }
}
public class SocialNetwork: BaseResponse
{
protected override string hrefPath { get { return "https://host/socialNetworkMemberships?profileId="; }}
public string SiteName { get; set; }
public string ProfileUrl { get; set; }
}
Services
public class ProfileService : Service
{
public object Get(Profiles request)
{
var testProfile = new Profile { Id= "123", GivenName = "Bob", SurName = "Smith", Gender = "Male", FavColor = "Red",
SocialNetworks = new List<BaseResponse>
{
new SocialNetwork { Id = "abcde", SiteName = "Facebook", ProfileUrl = "http://www.facebook.com/"}
}
};
if (!String.IsNullOrEmpty(this.Request.QueryString.Get("fields")) || !String.IsNullOrEmpty(this.Request.QueryString.Get("expand")))
return ServiceHelper.BuildResponseObject<Profile>(testProfile, this.Request.QueryString);
return testProfile;
}
}
public class SocialNetworkService : Service
{
public object Get(SocialNetworks request)
{
var testSocialNetwork = new SocialNetwork
{
Id = "abcde",
SiteName = "Facebook",
ProfileUrl = "http://www.facebook.com/"
};
if (!String.IsNullOrEmpty(this.Request.QueryString.Get("fields")) || !String.IsNullOrEmpty(this.Request.QueryString.Get("expand")))
return ServiceHelper.BuildResponseObject<SocialNetwork>(testSocialNetwork, this.Request.QueryString);
return testSocialNetwork;
}
}
Reflection Helper Class
public static class ServiceHelper
{
public static object BuildResponseObject<T>(T typedObject, NameValueCollection queryString) where T: BaseResponse
{
var newObject = new ExpandoObject() as IDictionary<string, object>;
newObject.Add("href", typedObject.href);
if (!String.IsNullOrEmpty(queryString.Get("fields")))
{
foreach (var propertyName in queryString.Get("fields").Split(',').ToList())
{
//could check for 'socialNetwork' and exclude if you wanted
newObject.Add(propertyName, typedObject.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(typedObject, null));
}
}
if (!String.IsNullOrEmpty(queryString.Get("expand")))
{
foreach (var propertyName in queryString.Get("expand").Split(',').ToList())
{
newObject.Add(propertyName, typedObject.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(typedObject, null));
}
}
return newObject;
}
}
Usually you can control the serialization of your DTOs by setting the DataMember attributes. With those attributes you can control if the property should have defaults or not.
Meaning if you simply do not define the property of the object you want to return, it should not be serialized and therefore will not be shown in the resulting Json.
ServiceStack internally uses the standard DataContract...Serializer, so this should be supported
Otherwise you could also make use of dynamic objects and simply compose your object at runtime, serialize it and send it back.
Here is a very very basic example:
var seri = JsonSerializer.Create(new JsonSerializerSettings() { });
using (var textWriter = new StringWriter())
{
var writer = new JsonTextWriter(textWriter);
dynamic item = new { Id = id };
seri.Serialize(writer, item);
return textWriter.ToString();
}