I'm trying to build an alternative of ModelState as when I return this from api like return BadRequest(ModelState) it doesn't have much information other than somethings gone wrong with the request. So I have decided to build an alternative which will eventually look like this:
{
"code": 1000,
"message": "Invalid request",
"fields": [
{
"name": "Password",
"message": "Password does not meet complexity requirements."
}
]
}
In order to accomplish this, I have the following models in my api project:
ErrorResponse.cs:
public class ErrorResponse
{
public string Code { get; set; }
public string Message { get; set; }
public Dictionary<string, Field> Fields { get; set; }
}
Field.cs:
public class Field
{
public string Name { get; set; }
public string Message { get; set; }
}
Thanks to using FluentResults I can determine the error from the service layer within my controller like so:
if (result.HasError<PasswordError>())
{
}
However I've realised that I now need to return the ErrorResponse but each Field has a name and the only way to know the name of the field is through the controller using:
nameof(RegisterRequest.Password)
Is there a way to do this in a clean generic way without using magic strings?
Related
I am trying to serialize JSON objects received from an API in a cli app. I'm having issues understanding how to create the objects in .NET for JSON objects which have an indented structure.
For example, this is fine:
{"status": "ok" }
public class Success
{
public string status { get; set; }
}
But something like this is where I'm stuck and both of the examples from the below return null when the client API receives them.
[
{
"id": "some_uuid_string_1",
"message": "hello"
},
{
"id": "some_uuid_string_2",
"message": "world"
}
]
Attempted solution
public class Received
{
public Dictionary<string,string> received { get; set; }
}
Alternatively I also tried a simpler structure, leaving out the explicit names and just using the IDs and values, which is closer to what my app requires and lets me make smaller requests.
{
"some_uuid_string_1": "hello",
"some_uuid_string_2": "world"
}
For this example I tried this, a list of key value pairs in the form of a dictionary.
public class Message
{
public Dictionary<string,string> message { get; set; }
}
public class Received
{
public List<Message> received { get; set; }
}
How can I create objects in C# for these two structures? One indented with set names and one 'generic' with no set names.
public class MyClass
{
public string id { get; set; }
public string message { get; set; }
}
[
{
"id": "some_uuid_string",
"message": "hello"
},
{
"id": "some_uuid_string",
"message": "world"
}
]
Deserializes to a List<MyClass> or MyClass[]
{
"some_uuid_string_1": "hello",
"some_uuid_string_2": "world"
}
Deserializes to
public class MyClass
{
public string some_uuid_string_1 { get; set; }
public string some_uuid_string_2 { get; set; }
}
or Dictionary<string, string>
The reason your Received class solution didn't work is because it is expecting a JSON property of received, as your class has a property named received, but the JSON does not.
This is the same issue with your Message class. Your class has property message whereas your JSON does not.
create a class
public class MessageId
{
public string id { get; set; }
public string message { get; set; }
}
you can deserialize your json
using Newtonsoft.Json;
var messages=JsonConvert.DeserializeObject<List<MessageId>>(yourJson);
This question sounds very trivial but I couldn't find it on internet. Let's say I am getting response like following json
{
"status": 1,
"msg": "1 out of 2 Transactions Fetched Successfully",
"transaction_details": {
"f9605b13-c300-4d11-b": {
"mihpayid": "14019310624",
"txnid": "f9605b13-c300-4d11-b",
"mode": "UPI",
"status": "success",
"App_Name": "PhonePe"
},
"546576": {
"mihpayid": "Not Found",
"status": "Not Found"
}
}
}
My problem is How do I make a Model in C# (So that I can deserialize this response)?
I tried this one -
public class TransactionDetails
{
[JsonProperty("txnid")]
public string TransactionId;
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("mode")]
public string Mode { get; set; }
[JsonProperty("mihpayid")]
public string MiPayId { get; set; }
[JsonProperty("amt")]
public string amount { get; set; }
[JsonProperty("App_name")]
public string AppName { get; set; }
}
public class ResponseBody
{
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("msg")]
public string Message { get; set; }
[JsonProperty("transaction_details")]
public List<TransactionDetails> Transactions { get; set; }
}
I know the problem, Problem is "transaction_details" is not a List, It is object of object.
How do I model that, The number of Keys and name of the Keys are not known!!
Please help me!
and sorry if the question is too trivial.
Any time you are dealing with a JSON structure where the keys are not static (i.e. the key is a unique identifier, date/time, etc.) you will need to use a Dictionary. You will still have the benefit of using a strongly-typed value, and you can perform any validation operations on the key to handle different formats as necessary.
I have to work with an API which handles error responses like this:
{
"error": {
"code": 3,
"message": "error message"
}
}
And success respones like this:
{
"data": {
"key": "value"
}
}
Error respones will always contain a code (integer) and a message (string), where as success respones can be different a lot ranging from just a few key-value-pairs to many objects and arrays.
I have created classes for every success "data" section and I can parse them successfully. Now I struggle with the simple part to determine if the response I got is actually an error or a success response.
My Idea was to create these classes:
public class APIResponse
{
[JsonProperty("error")]
public APIResponseError Error { get; set; }
[JsonProperty("data")]
public string Data { get; set; }
}
public class APIResponseError
{
[JsonProperty("code")]
public int Error { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
And to serialize to the class APIResponse. This works only for error responses (kinda obvious) as the data responses are more than just a string which the APIResponse.Data actually is. My idea was to not deserialize the data field and just store it as a string in APIResponse.Data. Then, when I check and see that error is null, I would deserialize the APIResponse.Data property with the correct class. But how can I do this?
You can set type of Data property to JToken:
public class APIResponse
{
[JsonProperty("error")]
public APIResponseError Error { get; set; }
[JsonProperty("data")]
public JToken Data { get; set; }
}
And deserialize later with ToObject:
myCorrectResponse.Data.ToObject<ExpectedDataType>()
But I highly doubt that you will be sent any data in case of error response so I would recommend making APIResponse generic (where T could be object, array, etc.):
public class APIResponse<T>
{
[JsonProperty("error")]
public APIResponseError Error { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
}
Which, in case of your example json will be used for example like:
class MyClass
{
[JsonProperty("key")]
public string Key { get; set; }
}
JsonConvert.DeserializeObject<APIResponse<MyClass>>(json);
Create a simple ASP.NET Core MVC web application from the template. Create a class called Human:
public class Human
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime Birthday { get; set; }
}
Now create a HumanController to post a human instance:
[HttpPost]
public Human Post([FromBody] Human human)
{
return human;
}
Using Fiddler or PostSharp (or any other client) post these JSON objects to this service:
{
"Name": "someone"
}
and
{
"Name": "someone",
"Age": "invalid age"
}
and
{
"Name": "someone",
"Birthday": null
}
Since Birthday can't be null and "invalid age" can't be parsed as valid model properties, what we get in our service parameter is null. That has proved to be hard to debug.
Is there a way that we can configure ASP.NET Core MVC to either only partially bind models as much as it can, or to somehow let us hook into its default behavior so that we can catch the exception and notify client about wrong data it has sent?
I think if you add Proper Attributes to your Model you can Handel it and return Proper Error Message to Clients.
For example you can put some Requirments on Name like :
public class Human
{
[Required(ErrorMessage ="")]
[StringLength(10)]
public string Name { get; set; }
public int Age { get; set; }
public DateTime Birthday { get; set; }
}
In this way you can Check your model and if it is not valid return BadRequest like :
public IActionResult Index(Human model)
{
if (!ModelState.IsValid)
return BadRequest("message");
//to do
return View();
}
Code:
[HttpPost("user/register")]
public IActionResult Register([FromBody] User user)
{
if (user?.Name is null || user?.Password is null)
{
return BadRequest(new {message = "User is null and/or name and password are not provided"});
}
else
{
// Add to db
return Json(user);
}
}
Also the User class:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public string Role { get; set; }
}
It should basically get a user and add it to the database. I tried sending this json:
{ "Name": "Batman", "Password": "IronmanSucks"}
The app caught the request, but the user object was null. I also tried with:
{ "user": { "Name": "Batman", "Password": "IronmanSucks"} }
But according to this documentation, the first json should have worked fine.
Here is a link to an example http request in postman
Does this have to do with the headers or is it a bug in .NET Core 2.0?
This can only happen if the type does not have a parameterless constructor, thus this can be simply fixed by adding such.
I believe that the Model is coming up as invalid hence why it is null.
You should try adding a [BindNever] attribute into the User class for the Role and Guid properties, seeing as you aren't using them.
If that doesn't work you may try using extended classes like so:
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
public class DataUser : User
{
public Guid Id { get; set }
public string Role { get; set; }
}
If you're using MVC Core instead of MVC, make sure you add Json Formaters (from Microsoft.AspNetCore.Mvc.Formatters.Json). In your Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.AddJsonFormatters();
}
This should help the [FromBody] to de-serialize your Post content