Deserializing a generic object from POST body - c#

I have a WebAPI endpoint that to takes in a generic object.
[HttpPost]
[ApiRoute("endpoint/{type}")]
public IHttpActionResult MyPostEndpoint(TypeEnum type, [FromBody] object myObject){}
We work on the object generically but then eventually convert it to our object type, but when we do we have to turn it into a JObject first, so grabbing the object looks like this:
var myfoo = ((JObject) object).ToObject<Foo>();
If I supply Foo directly as my POST parameter (e.g. [FromBody] Foo myObject) then it deserializes the incoming JSON to a Foo, but it won't deserialize to a generic C# object. Is there a way I can get it to deserialize to a generic C# object instead of leaving it a JObject so I can get myfoo like this instead?
var myfoo = (Foo) object;

As generic post method with data returned, I use. Than You can pass any class, so the request is more gerneric
public class Requests
{
//...
public async Task<ResultType> Create<ResultType>(string uri)
{
//TODO implementation of httpclient POST logic go here
var data = await results.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ResultType>(data);
return result;
}
Call method
List<foo> foos = new List<foo>();
Request requestToServer = new request();
Task.WaitAll(Task.Run(async =>(){
foos = await requestToServer.Create<Foo>("/foo");
}));
Now You can pass any predefined class

I think you can do like following to have a loosely typed method
public static class HttpRequestHelper
{
public static async Task<T> GetDataModelFromRequestBodyAsync<T>(HttpRequestMessage req)
{
dynamic requestBody = await req.Content.ReadAsStringAsync();
object blobModelObject = JsonConvert.DeserializeObject<object>(requestBody);
var blobModel = ((JObject)blobModelObject).ToObject<T>();
return blobModel;
}
}
and usage is like following:
var blobModel = await HttpRequestHelper.GetDataModelFromRequestBodyAsync<RequestBlobModel>(req);
Hope This Helps

.NET CLI
dotnet new web --name "GenericEndpointExample"
cd GenericEndpointExample
dotnet add package SingleApi
Program.cs:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// map generic endpoint
app.MapSingleApi("sapi",
// add your generic request handler
// for example, return the received data (already typed object)
x => Task.FromResult(x.Data),
// add assemblies for resolving received data types
typeof(MyClassName).Assembly, typeof(List<>).Assembly, typeof(int).Assembly);
app.Run();
Example request for type: MyClassName
POST /sapi/MyClassName
{"Name":"Example"}
Example request for generic: Dictionary<string,int?[]>
POST /sapi/Dictionary(String-Array(Nullable(Int32)))
{"key1":[555,null,777]}
GitHub repository with examples

Related

Json deserialize unknown or generic model type

This may not be possible.
Here is working code:
HttpResponseMessage playerResponse = await client.GetAsync("2018/export?TYPE=players&DETAILS=1&SINCE=&PLAYERS=9988%2C13604&JSON=1");
if (playerResponse.IsSuccessStatusCode)
{
var json = await playerResponse.Content.ReadAsStringAsync();
var o = JsonConvert.DeserializeObject<MFLPlayerAPIResult>(json);
playerList = o.PlayerData.MflPlayers.ToList();
}
The problem is I have lots of different models similar to MFLPlayerAPIResult like my code in the above, they all use different request strings. I am trying to build a function like this:
private async Task<Object> CreateListFromJson(Type jsonclass, string request, HttpClient client)
{
HttpResponseMessage response = await client.GetAsync(request);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var o = JsonConvert.DeserializeObject<jsonclass>(json);
return (o);
}
return (null);
}
where the Object returned would be the same model as the Type jsonclass used in the parameters, then I could use the function like this:
playerList = CreateListFromJson(MFLPlayerAPIResult, request, client).PlayerData.MflPlayers.ToList();
And use it multiple times over and over with different request strings and model types.
Is this possible?
If you know the type ahead of time you can just take in a generic parameter like so:
private async Task<T> CreateListFromJson<T>(string request, HttpClient client)
where T: class
{
var myObj = JsonConvert.DeserializeObject<T>(item);
//...
}
If you don't know the type until compile time you can call the deserialize at runtime by passing the type
private async Task<Object> CreateListFromJson(Type jsonclass, string request, HttpClient client)
{
var myObj = JsonConvert.DeserializeObject(item, jsonclass);
//...
}

C# HttpTriggered Azure funtion get content as interface from request

I am writing a simple http trigger azure function in c# as shown below.
[FunctionName("CommandReceiver")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]
HttpRequestMessage req,
[ServiceBus("cqrs-commands", AccessRights.Listen, Connection = "", EntityType = EntityType.Topic)]
IAsyncCollector<BrokeredMessage> messageTopic,
TraceWriter log)
{
var request = await req.Content.ReadAsAsync<IPayload>();
var brokeredMessage = new BrokeredMessage(request );
await messageTopic.AddAsync(brokeredMessage);
return req.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(delRequest));
}
From the client i am using the below code:
async Task<bool> IBus.RaiseCommand(Payload message)
{
var httpClient = HttpClientHelper.GetHttpClient(_azureSettings.Value.BaseUrl);
var jsonInString = JsonConvert.SerializeObject(message);
HttpResponseMessage response = await httpClient.PostAsync("api/CommandReceiver",
new StringContent(jsonInString, Encoding.UTF8, "application/json"));
return response.IsSuccessStatusCode;
}
public interface IPayload{
}
public class Payload : IPayload {
}
Using the above code i am not able to make it work. the azure function is throwing error at the line req.Content.ReadAsAsync<IPayload>;
Can anyone please help me the right way to achieve this?
Error i am getting is:
C# HTTP trigger function processed a request.
Exception while executing function: CommandReceiver
Microsoft.Azure.WebJobs.Host.FunctionInvocationException : Exception while executing function: CommandReceiver ---> Newtonsoft.Json.JsonSerializationException : Could not create an instance of type Commands.Model.IPayload. Type is an interface or abstract class and cannot be instantiated. Path 'Action', line 2, position 10.
Thanks
As #Thomas mentioned in the comment, serializer does not support Deserialize (and thus ReadAsAsync) calls with abstract types as generic parameter. Basically, just looking at JSON there's no way for serializer to know which concrete class it should instantiate to satisfy the desired interface requirement.
The obvious fix to this problem is to use Payload class directly:
await req.Content.ReadAsAsync<Payload>();
If you want to reuse the same Azure Function for multiple content types, you would need to either deserilize to JObject or dynamic and then act upon some properties, or add some sort of if-else logic based on HTTP request headers, route, etc.

Does JSON.net provide classes which accept HTTP response objects directly?

Answers such as this seem to go through several steps getting a string for the JSON content returned by an HTTP web request, which is then passed into JSON.net for deserialization.
Is there any shortcut that can be used e.g. do any API methods accept WebResponse or other intermediary objects? I couldn't see any but if they exist it would make for slightly neater code.
No, it does not. JSON.net doesn't mingle with HTTP directly. You have to do manual deserialization.
However, for the sake of neatness, might I suggest RestSharp? It makes my own code look a lot neater. You can implement ISerializer and inside of that, have your serialize method internally utilise all of Newtonsoft. RestClient handles all of the heavy lifting in regards to the WebResponse and will automatically deserialize for you. And no, it's not my library.
public class Employee
{
[JsonProperty(PropertyName = "employee_name")]
public string Name { get; set; }
}
private IRestResponse<Employee> GetEmployee()
{
var request = new RestRequest();
request.Resource = "/api/employees"
request.Method = Method.GET;
var response = Execute<Employee>(request);
}
public IRestResponse<T> Execute<T>(RestRequest request) where T : new()
{
// Pass in reusable ISerializer (can internally use Newtonsoft)
request.JsonSerializer = new JsonNetSerializer();
return client.Execute<T>(request); // Client is of type RestClient
}
Alternatively, you can do this and not implement a serializer at all, because it provides a Content property that you can pass into JSON.net as a string:
public IRestResponse<T> Execute<T>(RestRequest request) where T : new()
{
var response = client.Execute(request); // Client is of type RestClient
return JsonConvert.DeserializeObject<T>(response.Content);
}

MVC4 / Mocking Controller.Request

I´m currently working on a MVC4 Project. As I did a few refactorings the tests should be changed too.
In the first scenario a session hash had to be passed included within the URL. That being nasted, I decided to pass it to the server as a Request header.
Example:
[HttpPost]
public ActionResult Generate(ClipGenerateRequest request)
{
string hash = Request.Headers["hash"];
ClipGenerationResponse responseModel = new ClipGenerationResponse();
return Json(responseModel, JsonRequestBehavior.AllowGet);
}
The problem now seems to be that I'm unable to mock the Request object to a custom one as the Request object is a read-only object. Dynamically setting the header doesn't work, as the Request is null when performing Unit Tests. So the following won't work:
[TestMethod]
public void GenerateTest()
{
GenerationController target = new GenerationController(this.loginModelMock, this.clipTemplateModelMock, this.clipTemplateFieldModelMock);
target.Request = this.requestBase;
string templateId = "0";
ActionResult actual = target.Generate(templateId);
Assert.AreEqual(typeof(JsonResult), actual.GetType());
Assert.AreEqual(typeof(ClipGenerationResponse), ((JsonResult)actual).Data.GetType());
}
Where this.requestBase would be mock created with Moq.
public HttpContextBase CreateMockHttpContext()
{
var serverVariables = new NameValueCollection {
{ "UserHostAddress", "127.0.0.1" },
{ "UserAgent", "Unit Test Value" }
};
var httpRequest = new Moq.Mock<HttpRequestBase>();
httpRequest.SetupGet(x => x.Headers).Returns(
new System.Net.WebHeaderCollection {
{"hash", "somehash"}
}
);
httpRequest.Setup(x => x.ServerVariables.Get(It.IsAny<string>()))
.Returns<string>(x =>
{
return serverVariables[x];
});
var httpContext = (new Moq.Mock<HttpContextBase>());
httpContext.Setup(x => x.Request).Returns(httpRequest.Object);
return httpContext.Object;
}
Nor the static way would work:
target.Request.Headers["hash"] = "hash";
So, I'm wondering how this could be fixed nicely. I could always obtain the Request in the Constructor, set a class variable to hold the Request, and then mock the getter / setter for testing purposes, but I'd rather use a nicer way to do it. Though I don't seem to know a way to get it working.
PS: Please note that some class names may have been altered for preview.
Update
As you seem to be unable to mock HttpContext.Current.Request, I decided to mock the HttpContext.Current. Resulting in:
container.RegisterInstance<HttpRequest>(HttpContext.Current.Request);
Sadly this works for the API, but not for unit testing as HttpContext cannot e mocked as it's not an interface.
Initialization method
SomeApiTest.Controllers.LoginControllerTest.Initialize
threw exception. System.NotSupportedException:
System.NotSupportedException: Type to mock must be an interface or an
abstract or non-sealed class. .
The suggested way was by doing:
container.RegisterInstance<HttpRequestBase>(HttpContext.Current.Request);
But this doesn't work because Request cannot be cast to HttpRequestBase.
Which means, I do now have a way to unit test my code, but it will no longer be able to run..
Testing if this problem could be solved using a HttpRequestWrapper.
Looks like the following does work for testing:
HttpRequestBase requestBase = new HttpRequestWrapper(HttpContext.Current.Request);
container.RegisterInstance<HttpRequestBase>(requestBase);
But not for runtime. Because:
- Additional headers are not sent, such as: UserHostAddress, Custom Headers
with Postman is set with every request a custom header, named "hash". Using this method, it looks like these headers are no longer set.
Looks like headers are set when the method is called, but not when the Controller itself is created. Therefore Dependency Injection on this might not be suitable.
Ugly Temporary fix:
private AuthenticationHelper authenticationHelper = null;
private ILoginModel iLoginModel = null;
private IModuleModel iModuleModel = null;
private HttpRequestBase iRequestBase = null;
public LoginController(ILoginModel loginModel, IModuleModel moduleModel, HttpRequestBase requestBase)
{
this.authenticationHelper = new AuthenticationHelper(loginModel);
this.iLoginModel = loginModel;
this.iModuleModel = moduleModel;
this.iRequestBase = requestBase;
}
private HttpRequestBase GetRequestBase()
{
if (Request != null)
{
return Request;
}
else
{
return iRequestBase;
}
}
[HttpPost]
public ActionResult Login(LoginRequest login)
{
var ip = this.authenticationHelper.GetIpAddress(GetRequestBase());
var userAgent = this.authenticationHelper.GetUserAgent(GetRequestBase());
}
When within Controller you refer to something by static classes or Controller properties you usually shoot yourself in the boot, because you make your class not testable with unit test, as in your case. To avoid such situation is that your Controller gets HttpRequestBase injected with IoC. For instance with Autofac after you register module AutofacWebTypesModule your Controllers may accept in their constructor parameter of type HttpRequestBase with basically what you get with Register property. So you should make your controller look like this:
public class GenerationController : Controller
{
readonly HttpRequestBase _request;
public GenerationController(
HttpRequestBase request,
// Additional constructor parameters goes here
)
{
_request = request;
}
}
With Unity you should register factory for HttpRequestBase like this:
container
.RegisterType<HttpRequestBase>(
new InjectionFactory(c => new HttpRequestWrapper(HttpContext.Current.Request))
);
When you create your Controller within unit test it is extremely easy to mock HttpRequestBase as all its properties, along with Headers are virtual.
This is approach I usually use in my projects. However there is option to mock Controller Request property even without rewriting your original code. It involves use of Controller ControllerContext property:
Mock<HttpContextBase> httpContextMock = new Mock<HttpContextBase>();
Mock<HttpRequestBase> httpReguestMock = new Mock<HttpRequestBase>();
httpContextMock.SetupGet(c => c.Request).Returns(httpReguestMock.Object);
GenerationController controller = new GenerationController();
controller.ControllerContext = new ControllerContext(httpContextMock.Object, new RouteData(), controller);
controller.Index();
My mock for request looks:
var request = A.Fake<HttpRequestBase>();
var formCollection = new NameValueCollection();
A.CallTo(() => request.HttpMethod).Returns("POST");
A.CallTo(() => request.Headers).Returns(new System.Net.WebHeaderCollection
{
{"X-Requested-With", "XMLHttpRequest"}
});
A.CallTo(() => request.Form).Returns(formCollection);
A.CallTo(() => request.ApplicationPath).Returns("/");
For my needs it works. I'am using FakeItEasy to mock request, but I'm pretty sure that you can use Moq instead. I'm using FakeItEasy and Moq parallel in my project, and it works perfect.

Casting simple generic objects

I have a method that I created for simplifying using HttpClient calls. it uses the method HttpReponse.Content.ReadAsAsync().Result to get the response from the API.
This all works fine. My method looks something like this:
public static T ExecuteAPIGetRequest<T>(string url, Dictionary<string, string> parameters)
{
HttpClient client = new HttpClient();
//basic authentication
var t = new object();
string baseURL = "myurl";
//Execute request
HttpResponseMessage response = client.GetAsync(baseURL).Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return (T)t;
}
}
My question is, if the query fails it needs to return an empty type of T. This is fine if its a custom class I have written, but it does not work for objects like string or string[]. Any ideas?
Cheers
NCBL
try to return default(T)
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return default(T);
}
default will return null for reference types, and zeros numeric values int, double, etc.. and corresponding default values for custom struct and enum.
Daniel kindly noted one issue: if for reference types you want to return default object and not null, you should define generic constraint new T(). Now you can instantiate object of type T using call to parameter-less constructor. Full method is given below:
public static T ExecuteAPIGetRequest<T>(string url,
Dictionary<string, string> parameters)
where T : new()
{
HttpClient client = new HttpClient();
//basic authentication
string baseURL = "myurl";
HttpResponseMessage response = client.GetAsync(baseURL).Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return new T(); //returns an instance, not null
}
}
Now you will return default object for reference types, not null. Open type T can only take types, which have constructor by default (without parameters)
Can I make a suggestion that you consider an approach like this....
class Program
{
static void Main(string[] args)
{
var client = new HttpClient();
//basic authentication
string baseURL = "myurl";
var link = Link<Foo>.Create(baseURL);
var response = client.SendAsync(link.CreateRequest()).Result;
var myfoo = link.ProcessResponse(response);
}
}
public class Link<T>
{
public Uri Target { get; set; }
public static Link<T> Create(string url)
{
return new Link<T>() {Target = new Uri(url)};
}
public HttpRequestMessage CreateRequest()
{
return new HttpRequestMessage() {RequestUri = Target};
}
public T ProcessResponse(HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return new T(); //returns an instance, not null
}
}
}
public class Foo
{
}
By encapsulating the mechanics of creating the link into a static factory method and the handling of the response into the ProcessResponse method you get a similar level of re-usability, but you also get the benefits of reusing the same HttpClient. This allows you to actually take advantage of the DefaultRequestHeaders and it will stop HttpClient from keep closing the connection when it gets disposed.
Also, by avoiding wrapping the Async call inside a sync method, you can let the calling code decide whether to block for the result or handle it asynchronously. You are likely to run into deadlock issues at some point with the way you are currently using .Result.
This technique of creating a Link class to encapsulate specific behaviour relating to de-referencing a URL can be extremely useful. It is easy to add parameters that can be used to fill in URI templates. You can also use it to handle request bodies.

Categories