I have the following class
public class Customer
{
public Customer()
{
Project = new HashSet<Project>();
}
public uint Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public HashSet<Project> Project { get; set; }
}
And the following method
protected async Task<IRestResponse> ApplyRequest<T>(string resource,
Method method, T data) where T: class, new()
{
var client = new RestClient(_connectionConfig.BaseUrl);
client.FollowRedirects = false;
var request = new RestRequest(resource, method);
request.RequestFormat = RestSharp.DataFormat.Json;
request.AddParameter("application/json",
JsonConvert.SerializeObject(data), ParameterType.RequestBody);
//request.AddJsonBody(data);
//This also doesn't work
var response2 = await client.ExecuteTaskAsync<T>(request);
return response2;
}
Now if I call this method with Post method, the return statuscode is "created" (and it is actually created). However, the property IsSuccessful gives "false" and the error message is
"Unable to cast object of type 'SimpleJson.JsonArray' to type
'System.Collections.Generic.IDictionary`2[System.String,System.Object]'."
Is this usual, or am I doing something wrong?
Thanks
It's not successful because you had a serialisation error. I also see you logged it on GiTHub and got the same response. https://github.com/restsharp/RestSharp/issues/1064
Related
I have the following bit of code whihc sends a Http POST request to the server. The server reurns a 400 Bad request response along with a error object in the form of Json:
namespace MyApp.Shared.Dtos.Response
{
public class ErrorItem
{
public string Message { get; set; }
public string Tag { get; set; }
}
public class ErrorDto
{
public string Title { get; set; }
public List<ErrorItem> Errors { get; set; } = new();
}
}
namespace Accounting.Web.Services
{
public interface IHttpService
{
Task<T> Get<T>(string uri);
Task<T> Post<T>(string uri, object value, bool addBearerToken = false);
public ErrorDto Error { get; set; }
}
public class HttpService: IHttpService
{
private HttpClient _httpClient;
public ErrorDto Error { get; set; }
public HttpService(HttpClient httpClient)
{
_httpClient = httpClient;
_stateService = stateService;
}
public async Task<T> Post<T>(string uri, object value)
{
var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = new StringContent(JsonSerializer.Serialize(value), Encoding.UTF8, "application/json");
return await sendRequest<T>(request, addBearerToken);
}
private async Task<T> sendRequest<T>(HttpRequestMessage request)
{
using var response = await _httpClient.SendAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
var result = await response.Content.ReadAsStringAsync();
Error = JsonSerializer.Deserialize<ErrorDto>(result);
//..
}
else
{
//..
}
}
}
}
The result correctly recieves the following response from the server as a JSON string:
{"title":"Username or password is incorrect","errors":[]}
And I can confirm by inspecting var result, it has the above value.
However, It doesn't seem deserialize into the ErrorDto class as one would expect it to:
Error = JsonSerializer.Deserialize(result);
But I simply cannot see any problems with the code, it looks like it should be working.
*** UPDATE ***
My server API code returrns the JSOn using the same DTO class (It's a shared class) using the following code:
[HttpPost("authenticate")]
public ActionResult Authenticate(AuthenticateRequest loginRequest)
{
var auth = _userService.Authenticate(loginRequest);
ErrorDto error = new()
{
Title = "Username or password is incorrect"
};
if (auth.user == null || auth.token == null)
{
return BadRequest(error);
}
return Ok(auth.user.ConvertToDto(auth.token));
}
By default System.Text.Json is case-sensitive. There are multiple options to handle this, for example by providing corresponding JsonSerializerOptions:
var json = #"{""title"":""Username or password is incorrect"",""errors"":[]}";
var errorDto = JsonSerializer.Deserialize<ErrorDto>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
Or marking properties with corresponding JsonPropertyNameAttribute:
public class ErrorItem
{
[JsonPropertyName("message")]
public string Message { get; set; }
[JsonPropertyName("tag")]
public string Tag { get; set; }
}
public class ErrorDto
{
[JsonPropertyName("title")]
public string Title { get; set; }
[JsonPropertyName("errors")]
public List<ErrorItem> Errors { get; set; } = new();
}
UPD
From How to customize property names and values with System.Text.Json doc:
Note
The web default is camel case.
If you want to switch from camel case to the naming policy used for DTOs you can do the following:
builder.Services.AddControllers()
.AddJsonOptions(opts => opts.JsonSerializerOptions.PropertyNamingPolicy = null);
I am new to C# and .Net and I am struggling to understand this issue that I have. I feel like what I have is enough but not sure myself. SO if someone can just review the codes that I have below and tell me were to correct and what is wrong.Thank you
This is my Model Class
namespace github_project.Models
{
public class GithubItem
{
public int Id { get; set; }
public string UserName { get; set; }
public string FullName { get; set; }
public string City { get; set; }
public string ProjectName { get; set; }
public string Commits { get; set; }
public double Rating { get; set; }
public string AvatarUrl { get; set; }
}
}
and this is my database context
namespace github_project.Database
{
public class GithubContext : DbContext
{
public DbSet<GithubItem> Github { get; set; }
public GithubContext(DbContextOptions<GithubContext> options) : base(options)
{
}
public GithubItem ItemsList()
{
List<GithubItem> build = Build();
GithubItem itemsList = JsonConvert.DeserializeObject<GithubItem>(build);
return itemsList;
}
public List<GithubItem> Build()
{
var getData = GetGithubData();
return System.Text.Json.JsonSerializer.Deserialize<List<GithubItem>>(getData);
}
private string GetGithubData()
{
string username = "**test**";
var url = "https://api.github.com/users/" + username + "/repos?page=1";
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";
request.ContentType = "application/json";
request.UserAgent = "TestApp";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
}
}
public List<GithubItem> getGithub() => Github.Local.ToList<GithubItem>();
}
}
finally this is my controller
[HttpGet("/github")]
public GithubItem GetAll()
{
return _context.ItemsList();
}
I am making a request to github in order to get all the data and use it in my request. I am getting an here here of converting Collection.List to String on this method below:
public GithubItem ItemsList()
{
List<GithubItem> build = Build();
GithubItem itemsList = JsonConvert.DeserializeObject<GithubItem>(**build**);
return itemsList;
}
Can someone help me and and someone tell me what is wrong here??? Thank you
You cannot deserialize an object or convert an object from List<GithubItem> to single GithubItem. That is what you are doing.
As you can see, you have build:
List<GithubItem> build = Build();
This build variable is a List<GithubItem. Now you want to convert it to single using Deserialize of JsonConvert?
You can just get one record, whatever your requirements is using this code:
GithubItem itemsList = build.FirstOrDefault();
That would build fine. But this is just an example since I am not sure what is your requirement. If you need to filter your record, you can also pass a argument on FirstOrDefault:
GithubItem itemsList = build.FirstOrDefault(x => x.UserName == "John");
That would also work fine.
What I have:
I have an API where all non-success responses return an "IApiException"
public class ApiException : Exception, IApiException
{
public ExceptionType Type { get; set; }
public int ErrorCode { get; set; }
public HttpStatusCode Status { get; set; }
public string Code { get; set; }
public List<Error> Errors { get; set; } = new List<Error>();
public ApiException(ExceptionType type, Reason reason, string message, HttpStatusCode httpStatus)
{
Type = type;
Errors.Add(new Error { Message = message, Reason = reason.ToString() });
Status = httpStatus;
Code = httpStatus.ToString();
}
}
If I set the example in swashbuckle through an ISchemaFilter
it will put the same example on all model examples, so a 401 will return the same example as a 429.
What I want:
I want to be able to return different examples based on the status code.
Wha I've tried:
I tried creating an IOperationsFilter looking for the response keys and then setting either the response schema example or the response examples.
But it's is not displaying the models, at all.
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
foreach (var response in operation.responses)
{
if (response.Key == "401")
{
response.Value.description = "Error";
response.Value.schema.example = new
{
Type = ExceptionType.Error.ToString(),
Errors = new {Message = "Unauthorized", Reason = Reason.Unauthorized},
Status = HttpStatusCode.Unauthorized,
Code = HttpStatusCode.Unauthorized.ToString()
};
}
}
}
I am developing Web API for my client. They have suggestion that all response should be a common JSON structure.
{ Data:"", Status:true, Message:"" }
If error means
{ Error:"", Status:false, Message:"" }
Which is the best method to create a common JSON structure as returns.
Now I created a class having these properties. And created 2 classes from IHttpActionResult,Error.cs and Success.cs, From that the response is created and returned from the controller.
The thing is in my controller,
public IHttpActionResult GetNewsAndAnnouncements()
{
var data = newsAndAnnouncementsDataServices.NewsAndAnnouncements();
if (data != null && data.Count() > 0)
{
return new Success(Request, "News and Announcements Retrieved Successfully", data);
}
return new Error(Request, "No News and Announcements Found");
}
Error.cs
public class Error : IHttpActionResult
{
private readonly string _message;
private readonly HttpRequestMessage _request;
private IErrorResponseModel errorResponse;
public Error(HttpRequestMessage request, string message)
{
_message = message;
_request = request;
errorResponse = new ErrorResponseModel();
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
errorResponse.Message = _message;
errorResponse.Status = false;
errorResponse.Error = _message;
var response = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new ObjectContent<object>(errorResponse, new JsonMediaTypeFormatter()),
RequestMessage = _request
};
return Task.FromResult(response);
}
}
Success.cs
public class Success : IHttpActionResult
{
private readonly string _message;
private readonly object _data;
private readonly HttpRequestMessage _request;
private IDataResponseModel dataResponse = new DataResponseModel();
public Success(HttpRequestMessage request, string message, object data)
{
_message = message;
_request = request;
_data = data;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
dataResponse.Message = _message;
dataResponse.Status = true;
dataResponse.Data = _data;
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ObjectContent<object>(dataResponse, new JsonMediaTypeFormatter()),
RequestMessage = _request
};
return Task.FromResult(response);
}
}
DataResponseModel.cs
public class DataResponseModel : Mobility.Common.IDataResponseModel
{
public object Data { get; set; }
public string Message { get; set; }
public bool Status { get; set; }
}
ErrorResponseModel.cs
public class ErrorResponseModel : Mobility.Common.IErrorResponseModel
{
public object Error { get; set; }
public string Message { get; set; }
public bool Status { get; set; }
}
Is this a right method. I need suggestion. Is there any other way to achieve this. I heard about delegating handler something. But I don't have much idea on these.
Please help me.
Another solution to this problem is to hook into the ASP.NET pipeline using custom handlers to build a common response object.
For instance:
[DataContract]
public class ApiResponse
{
[DataMember]
public string Version { get { return "1.2.3"; } }
[DataMember]
public int StatusCode { get; set; }
[DataMember(EmitDefaultValue = false)]
public string ErrorMessage { get; set; }
[DataMember(EmitDefaultValue = false)]
public object Result { get; set; }
public ApiResponse(HttpStatusCode statusCode, object result = null, string errorMessage = null)
{
StatusCode = (int)statusCode;
Result = result;
ErrorMessage = errorMessage;
}
}
Have a look at this post for a reference implementation http://www.devtrends.co.uk/blog/wrapping-asp.net-web-api-responses-for-consistency-and-to-provide-additional-information
I have a method that ts returning Array. I have problem with one class that cant convert the return type to array.
This is my method:
public Model.BaseType[] returnPayment_Gateway()
{
IncomingWebRequestContext request = WebOperationContext.Current.IncomingRequest;
WebHeaderCollection headers = request.Headers;
var settings = new DataContractJsonSerializerSettings { EmitTypeInformation = EmitTypeInformation.Never };
MemoryStream stream1 = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Model.BaseType),settings);
Model.Payment_Gateway[] allRecords = null;
if (headers["ServiceAuthentication"] != null)
{
string ServiceAuthentication = headers["ServiceAuthentication"].ToString();
bool serviceAuth = Service_Authentication(ServiceAuthentication);
DAL.DataManager dal = new DAL.DataManager();
if (serviceAuth == true)
{
allRecords = dal.Get_Payment_Gateway();
}
}
else
{
// Create a new customer to return
return new Model.ReturnResponse() { StatusCode = 201, StatusDescription = "Authentication Fails" };
}
return allRecords;
}
My problem is in else part, not sure how I can convert Model.ReturnResponse() to array now I am getting this error:
cannot implicitly convert type ReturnResponse to Model.BaseType[]
in case you like to see my 3 classes:
This is Base class:
[Serializable]
[DataContract]
[KnownType(typeof(Payment_Gateway))]
[KnownType(typeof(ReturnResponse))]
public class BaseType
{
}
this is Payment_Gateway class:
[DataContract]
public class Payment_Gateway:BaseType
{
[DataMember]
public int Payment_Gateway_ID { get; set; }
[DataMember]
public string Payment_Gateway_Name { get; set; }
[DataMember]
public string Payment_Gateway_URL { get; set; }
[DataMember]
public string Payment_Gateway_Description { get; set; }
and this is ReturnResponse class:
[DataContract]
public class ReturnResponse:BaseType
{
[DataMember]
public int StatusCode { get; set; }
[DataMember]
public string StatusDescription { get; set; }
}
Instead of trying to return a single ReturnResponse:
return new Model.ReturnResponse()
{ StatusCode = 201, StatusDescription = "Authentication Fails" };
Return an array, with the single element in it, since that's what your method is expected to return:
return new[] {
new Model.ReturnResponse()
{ StatusCode = 201, StatusDescription = "Authentication Fails" }
}
Instead of the hierarchy you have designed here, it would be easier if you encapsulate your return data in a response object that indicate a response type (success or failure) and the resulting data.
that class could look like the following
public enum ResponseTypes
{
Success,
Failure
}
public class Response
{
public ResponseTypes ResponseType {get; set;}
public Exception Error {get;set;}
public BaseType[] Data {get;set;}
}
this way it works like an envelope to your actual data and provides state to the caller to know if the call succeeded or not, and maybe for what reason, before reading the data (result)
its the same way WCF acts under the hood (in SOAP, at least) that it surrounds your data contract with a message envelope/enclosure to add more metadata for the caller to interpret the call status.