Using RestSharp I'm building an API to perform CRUD operations given a datatype/object.
My CrudAbstract class is generic and has the following:
public virtual async Task<keyType> Post(dto item)
{
try
{
var request = await _client.GetRequestAsync(_path);
request.Method = RestSharp.Method.POST;
request.AddJsonBody(item);
var result = await _client.ExecuteRequestAsync<keyType>(request);
return result;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
throw new Exception("Was not able to process crud post operation.");
}
My WebClient class has the following:
Entities = new CrudAbstract<DtoEntity, int>("/entities", this); // in constructor
// So the keyType from definition above is int (Int32)
The post method in this class is
public async Task<T> ExecuteRequestAsync<T>(IRestRequest request)
{
try
{
var response = await GetClient().ExecuteTaskAsync<T>(request);
// Exception occurs here. The above statement is unable to finish.
var data = response.Data;
return data;
}
catch (Exception e)
{
// Log exception
}
throw new Exception("Was not able to process restclient execute async method.");
}
My Api EntitiesController has the following:
public int Post(DtoEntity value)
{
using (var db = // some database...)
{
try
{
// Upsert object into database here
//... value.Id no longer null at this point
/*
The problem occurs here. I only want to return the ID of the object
(value.Id). I do not want to return the whole object (value)
*/
return value.Id;
}
catch (Exception e)
{
// Log exception
}
}
throw new Exception("Was not able to process entities post method.");
}
The exception I get is:
Unable to cast object of type 'System.Int64' to type
'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
This is basically saying it is unable to cast the object int (which I have returned in the post with value.Id) to a DtoEntity object (which is the actual object on which CRUD operations were performed).
What am I doing wrong?
I have placed typeof and .getType() onto each keyType, T, and value.Id, and all of them are Int32. Is it in the RestSharp library that the problem occurs? At what stage is there a casting of int to a DtoEntity in the line:
var response = await GetClient().ExecuteTaskAsync<T>(request);
Note: when I change the return type of the post method in my controller to DtoEntity and change the value.Id to just value it works. The response is received, and the response.Data is the DtoEntity object.
I've seen similar questions here but not found a solution yet.
I believe you've spotted a bug in RestSharp. RestSharp internally uses a JSON parser called SimpleJson (borrowed from the Facebook .NET SDK). It appears that this parser is correctly deserializing the response body to a number (since JSON is untyped it uses Int64 to be safe), but the RestSharp's JsonDeserializer class attempts to cast this result to an IDictionary in the first line of this method:
private object FindRoot(string content)
{
var data = (IDictionary<string, object>)SimpleJson.DeserializeObject(content);
if (RootElement.HasValue() && data.ContainsKey(RootElement))
{
return data[RootElement];
}
return data;
}
I think your options are:
File an issue with the RestSharp team.
Get the raw string body and cast it to an int yourself (may be the path of least resistance).
Try a different library.
Related
It's been a whole day and I can't able to find a solution.
My Controller's ActionMethod is working fine when it calls from PostMan.
But when I call it from my Unit Test Method, it keeps returning null.
Here is my code.
I already found an answer on StackOverflow:
https://stackoverflow.com/a/56498657/11425180
But this is not resolving my issue.
Here is my ActionMethod
[HttpPost("register")]
public async Task<IActionResult> RegisterUser([FromBody] CreateUserRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
// Map request with dto and give to service
CreateUserRequestDto createDto = _mapper.Map<CreateUserRequestDto>(request);
CreateUserResponseDto response = await _userService.CreateUser(createDto);
if (response.IsSuccess)
{
Success success = new Success(message: SuccessMessages.UserCreated, data: response);
return Ok(success);
}
Error error = new Error(message: ErrorMessages.UserRegistrationFailed, description: response.Error.Description);
return BadRequest(error);
}
catch (Exception ex)
{
return HandleException(ex);
}
}
Here is my test Class
public class MockAccountControllerTests
{
readonly UserController _accountController;
public MockAccountControllerTests()
{
Mock<IMapper> _mockMapper = new Mock<IMapper>();
Mockers.InitializeMappers(_mockMapper);
UserManager<AppUser> _mockUserManager = Mockers.MockUserManager<AppUser>().Object;
UserRepository _mockUserRepository = new Mock<UserRepository>(_mockUserManager, _mockMapper.Object).Object;
UserService _mockUserService = new Mock<UserService>(_mockUserRepository).Object;
_accountController = new Mock<UserController>(_mockUserService, _mockMapper.Object).Object;
}
[Fact]
public async Task RegisterUser_NullUserNamePassword_ThrowsException()
{
CreateUserRequest request = RequestHelpers.CreateUserRequest(null, null);
IActionResult result = await _accountController.RegisterUser(request);
ObjectResult badRequest = result as ObjectResult;
Assert.NotNull(badRequest);
Assert.True(badRequest is BadRequestObjectResult);
Assert.Equal(StatusCodes.Status400BadRequest, badRequest.StatusCode);
Assert.NotNull(badRequest.Value);
Assert.IsType<Error>(badRequest.Value);
}
}
At this line in my Test Method
var result = await _accountController.RegisterUser(request);
The result is null
I also tried to assign a value of BadRequest to a variable like this
var a = BadRequest("Test Message");
The a is also null in this case.
How this will be corrected?
Am I doing something wrong in my ActionMethod or TestMethod?
Kindly review.
This line is the culprit:
_accountController = new Mock<UserController>(_mockUserService, _mockMapper.Object).Object;
Don't mock your system under test, because then you're not testing your code, but simply whether Moq does what it's supposed to do. And it does: it returns default values (null for reference types) for method calls that aren't SetUp().
So instead initialize it to an actual instance of your controller:
_accountController = new UserController(_mockUserService, _mockMapper.Object);
See also: Test controller logic in ASP.NET Core on learn.microsoft.com.
The only possible explanation is that whatever the type of result, it's not derived from ObjectResult, so casting it to ObjectResult via as results in a null value being returned.
The thing is that, I don't see any output that returns something that doesn't derive from ObjectResult, except in your catch block where you're returning HandleException(ex). It's not clear what the type returned is here, so I can only assume that that's where your problem lies. In short, that needs to return an ObjectResult type. Things like BadRequestResult without Object in their names derive from StatusCodeResult, which shares no lineage with ObjectResult.
It's also worth mentioning that once you get past this assertion, your next will fail. Since you're casting whatever the result is to ObjectResult and saving it to badRequest, the assertion Assert.True(badRequest is BadRequestObjectResult) will always fail, because it will always be ObjectResult, not BadRequestObjectResult.
I would like to an async method "UpdateAsync" return custom exception message when PutAsync method is invoked. What I do now is mock the class which is PutAsync belong to, and then I setup the method and give the parameter. I also use Throws to custom exception message.
The problem is when I run this
var result = await this.repository.UpdateAsync(new EndPoint(new Uri(testUrl), HttpMethod.Put), JObject.FromObject(new object()), this.exceptionProcessor);
The PutAsync keep running without return exception.
Here is the code.
Mock<RestClient> rc = new Mock<RestClient>();
rc.Setup(x => x.PutAsync(new Uri(testUrl), JObject.FromObject(new object()), new NameValueCollection()))
.Throws(new Exception("TestMessage"));
var result = await this.repository.UpdateAsync(new EndPoint(new Uri(testUrl), HttpMethod.Put), JObject.FromObject(new object()), this.exceptionProcessor);
Assert.IsTrue(result.ErrorMessages.GetValue(string.Empty).Equals("TestMessage"));
here is the main part of UpdateAsync, when process goes here, it will enter GetClient() first and then jump to Exception direct. This test was wrote using Shimes, but we don't want to use Shimes anymore, therefore I need to use another way to do.
public virtual async Task<GenericOperationResult<object>> UpdateAsync(EndPoint endpoint, JContainer obj, IExceptionProcessor exceptionProcessor, NameValueCollection headers){
if (endpoint.ActionMethod == HttpMethod.Put)
{
result = await this.GetClient().PutAsync(endpoint.Url, obj, headers);
}
else if (endpoint.ActionMethod == HttpMethod.Post)
{
result = await this.GetClient().PostAsync(endpoint.Url, obj, headers);
}
else
{
throw new ConfigurationException("Update supports only POST or PUT verbs. Check endpoint configuration.");
}
return new GenericOperationResult<object>(200, result);
}
You are instantiating new objects in your setup, which are different from the objects you are instantiating in your call to UpdateAsync, so they won't match and the Mock object won't throw the exception. You could instead setup the mock to throw the exception if objects of the correct types are passed in, with the Url param also checking it has the testUrl, for example:
rc.Setup(x => x.PutAsync(It.Is<Uri>(u => u.OriginalString == testUrl), It.IsAny<JObject>(), It.IsAny<NameValueCollection>())
.ThrowsAsync(new Exception("TestMessage"));
I am trying to deserialize a JSON string into a large object; I'm using Angular to send my data to a controller. Data is present and correct when it arrives in the controller, but after deserialization, it's null. I've looked at some questions already, like this one here:
Deserialization of JSON.Net returns 'null'
Based on the answer to the question above, and the comment from the user who wrote the question, my code should work, but it doesn't.
Here's my code:
[HttpPost]
public OnlineOrderValidation Post(HttpRequestMessage request)
{
Task<string> result = request.Content.ReadAsStringAsync();
var json = result.Result;
OnlineOrder order = new OnlineOrder();
order = JsonConvert.DeserializeObject<OnlineOrder>(json);
// Save first
_orderFacade.UpdateCurrentOrder(CacheStore, order);
var validation = _orderFacade.ValidateOrder(order);
return validation;
}
The OnlineOrder class should accept all of the data from JsonConvert.DeserializeObject(json), but the values are either 0, null, or false.
I've tried DeserializeAnonymousType(string json, T anonObject) and JavasScriptSerializer.Deserialize(string data) as well, and they both yield an empty OnlineOrder object with null values.
I've also tried passing the object to the controller like this:
public OnlineOrderValidation Post([FromBody]string ooJson)
{
var order = JsonConvert.DeserializeObject<OnlineOrder>(ooJson);
//work with order object...
}
and like this
public OnlineOrderValidation Post([FromUri]string ooJson)
{
var order = JsonConvert.DeserializeObject<OnlineOrder>(ooJson);
//work with order object...
}
I wonder if the problem lies with 'ReadAsStringAsync()'. I noticed that it spits out each property on OnlineOrder out of order. The async part of this method must add each field/value pair as it finishes parsing the string, and since the first field in result doesn't match the first field on the OnlineOrder object, maybe JsonConvert.DeserializeObject() just stops execution.
Do I need to make a wrapper class for OnlineOrder and deserialize to that? Do I need to reorder the fields in my 'result' object before deserializing? Is there something else I'm doing wrong? Thanks all!
EDIT: Here is my JSON, as requested. The actual object is much larger than this, but you get the idea. The JSON string has valid data, but is either nulled or zeroed out after deserialization.
{"isTMIOfferEligible":false,
"tmiOfferCode":null,
"tmiUpgradeOfferCode":null,
"id":"a34mdxdivt0svmu1okucidbi",
"accountNumber":"111111111",
"transferAccountNumber":"222222222",
"serviceAddress":
{"address":"3922 N STREET AVE",
"addressLine":"3901 N STREET AVE",
"suite":"SYNCRD",
"city":"SOOFOO",
"state":"SD",
"zip":"57000",
"franchise":"111",
"market":"1 "},
"transferAddress":
{"disconnectDate":null,
"transferFromAddress":
{"address":"1234 SOME PLACE",
"addressLine":"1234 SOME PLACE",
"suite":"SYNCRD",
"city":"SOOFOO",
"state":"SD",
"zip":"57000",
"franchise":"123",
"market":"Z "
}
}
}
EDIT 2
I've updated my code to deserialize the JSON string from a custom, async method, and instantiate OnlineOrder when it's assigned:
try
{
var orderTask = ReadStringAsynchronously(request);
OnlineOrder order = orderTask.Result;
//work with order object
}
catch (Exception ex) { //...}
private async Task<OnlineOrder> ReadStringAsynchronously(HttpRequestMessage request)
{
try
{
var json = await request.Content.ReadAsStringAsync();
OnlineOrder orderAsync = JsonConvert.DeserializeObject<OnlineOrder>(json);
return orderAsync;
}
catch (Exception)
{
throw new Exception("Error occurred while reading string asynchronously.");
}
}
The order object on the task is still null.
The issue, in this case, was in the JSON string itself. There was HTML nestled within the JSON string that was not being encoded. Before posting to the server, I used encodeURIComponent(value) in a custom method:
function encodeOrderHtml(order) {
debugger;
var o = order;
//scrub HTML from product options
if (o && o.serviceDiscount) {
var sd = o.serviceDiscount;
if (sd.description !== "" && sd.description !== null) {
sd.description = encodeURIComponent(sd.description);
}
if (sd.descriptionNew !== "" && sd.descriptionNew !== null) {
sd.descriptionNew = encodeURIComponent(sd.descriptionNew);
}
if (sd.displayNameNew !== "" && sd.displayNameNew !== null) {
sd.displayNameNew = encodeURIComponent(sd.displayNameNew);
}
if (sd.name !== "" && sd.name !== null) {
sd.name = encodeURIComponent(sd.name);
}
}
return o;
}
I was able to get valid data to the server by posting the object in the body of the request.
I'm building a .Net Console application to read information in a DocumentDB. The console app has data coming from an EventHub and inserts/updates recent data as it comes into the cloud.
I am trying to read a singular document from DocumentDB and I can confirm that the Document Exists prior to requesting the Document.
if (DocumentDBRepository<DocumentDBItem>.DoesItemExist(name))
{
device = await DocumentDBRepository<DocumentDBItem>.GetItemAsync(name);
}
I used this tutorial from Microsoft about building a Repository for accessing the DocumentDB records, and was successful at using almost all of the methods. I can update/delete/query the DB but I cannot read a singular Item.
First it was throwing an exception requesting a PartitionKey. So I modified the method to add the PartitionKey being used by the DB to the request. As soon as I added the PartitionKey it throws another exception with a message, "Resource Not Found"
public static async Task<T> GetItemAsync(string id)
{
try
{
RequestOptions options = new RequestOptions();
options.PartitionKey = new PartitionKey("DeviceId");
Document document = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), options);
return (T)(dynamic)document;
}
catch (DocumentClientException e)
{
if (e.StatusCode == HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}
}
I've already modified my calls to use "GetItemsAsyc" and get an IEnumerable of Documents and get the First item in the list, but it doesn't make sense why I can use all of the other methods from the tutorial but this one continues to throw exceptions saying, "Resource Not Found".
Exception I'm getting:
"Message: {\"Errors\":[\"Resource Not Found\"]}\r\nActivityId: e317ae66-6500-476c-b70e-c986c4cbf1d9, Request URI: /apps/e842e452-2347-4a8e-8588-2f5f8b4803ad/services/2c490552-a24d-4a9d-a786-992c07356545/partitions/0281cfdd-0c60-499f-be4a-289723a7dbf9/replicas/131336364114731886s"
As shown in the documentation, you need to provide the value of the partition key, not the name of the field which stores the partition key. Thus, you need to add the device Id as a parameter to your method and pass its value in to the PartitionKey constructor.
From the example:
// Read document. Needs the partition key and the ID to be specified
Document result = await client.ReadDocumentAsync(
UriFactory.CreateDocumentUri("db", "coll", "XMS-001-FE24C"),
new RequestOptions { PartitionKey = new PartitionKey("XMS-0001") });
So for your code:
public static async Task<T> GetItemAsync(string id, string deviceId)
{
try
{
RequestOptions options = new RequestOptions();
options.PartitionKey = new PartitionKey(deviceId);
Document document = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), options);
return (T)(dynamic)document;
}
catch (DocumentClientException e)
{
if (e.StatusCode == HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}
}
I am trying to build a generic extension method that can call a member function of a class dynamically and return a specified type. For some background, this is the general problem:
I am using Autorest to generate some client libraries for a swagger API. Because some GET routes within the API return different objects depending on the HTTP status code of the response, the method invocation returns object and the developer is responsible for casting the object themselves. I am trying to create a convenient wrapper for doing this cast in a generic way.
Here is an example of a typical function signature that would be wrapped up:
object IPet GetPets(string petName)
Note that this method might return a number of object types, depending on the HTTP status code. For instance, 200 might return a Dog object but 404 might return a Cat object.
This would be invoked through an Autorest generated client library like this:
AnimalApi animalClient = new AnimalApi(new Uri("http://myanimals.com"));
object pet = animalClient.Pet.GetPets("dog");
if(pet is Dog) {
Console.WriteLine("Dog!");
} else {
Console.WriteLine("Not Dog");
}
I would like to scoop up this manual casting functionality into something a bit more intuitive, here is what I am thinking:
AnimalApi animalClient = new AnimalApi(new Uri("http://myanimals.com"));
string petType = "Dog";
Dog pet = animalClient.Pet.CallMethod<IPet, Dog, string>( (api,type) => api.GetPets(type), petType);
In this scenario, any return other than objects of type 'Dog' would cause an exception to be thrown. Here is my attempt at implementation:
public static Tout CallMethod<Tin, Tout>(this Tin client, Expression<Action<Tin, Targ>> apiCall, params object[] args)
where Tout : class {
MethodCallExpression providedMethod = apiCall.Body as MethodCallExpression;
if(providedMethod == null) {
throw new ApplicationException("Invalid method call provded");
}
var method = providedMethod.Method;
object responseData;
try {
// Call object-returning function
responseData = method.Invoke(client, args);
} catch(Exception error) {
if(error.InnerException is HttpOperationException) {
// Unknown error occurred
var ex = error.InnerException as HttpOperationException;
object content = JsonConvert.DeserializeObject(ex.Response.Content);
throw new ServiceException(ex.Response.StatusCode + ": "
+ JsonConvert.SerializeObject(content));
} else {
throw error.InnerException;
}
}
// Return formatted object if successful
if(responseData is Tout) {
return responseData as Tout;
// Otherwise throw
} else {
// Deal with known error object responses
if(responseData is ErrorResponse) {
var error = responseData as ErrorResponse;
throw new ServiceException(error);
} else {
// Unknown error occurred
throw new ServiceException("Unknown response was received: "
+ JsonConvert.SerializeObject(responseData));
}
}
}
The problem I have here is of passing the function and arguments into the generic extension method. Without knowing the various possible numbers of arguments that might be required by the various API calls, how can I define Expression<Action<Tin, Targ>> generically? It seems to me like I would have to replicate this function with Expression<Action<T1, T2, T3>> and so on to accommodate varying length argumen lists.
I want an expressive way for people to interact with the API, such that it is easy to see what is happening. However, this mechanism should be robust to various API changes down the road. My current goal is to provide a way to encapsulate common object casting and error checking operations. Is there a better way to do this? For the moment I am working under the assumption that the server side swagger doc cannot change.