JsonIgnore attribute is not working in asp.net - c#

My model is
using Newtonsoft.Json;
...
public class User
{
public int Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
[JsonIgnore]
public string Email { get; set; }
public User()
{
}
}
and controller method is
[HttpPost]
public async Task<ActionResult<User>> PostUser(User user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
return CreatedAtAction("GetUser", new { id = user.Id }, user);
}
I am using adding newtonsoft to a project in Program.cs like that:
builder.Services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
And when i try to post user in postman, i getting validation error neither i am including email field or not:
"errors": {
"Email": [
"The Email field is required."
]
},
I tried different ways of including newtonsoft in Program.cs, also i tried to make my model with [DataContract] excluding email field. If i remove builder.Services.AddMvc().AddNewtonsoftJson() then with email field validation is passing, but without it still failing. using System.Text.Json.Serialization not working too. What can i do?

I fixed validation problem after making string nullable,
public string? Email { get; set; }

Related

Web API HTTPGet for multiple attributes?

We have a Web API written in DotNet Core 3.1.402 (I am new to DotNet Core and WebAPI).
We use SqlKata for Database processing.
We have an Account model that has AccountID, AccountName, AccountNumber, etc.
We would like to get an Account by different attributes, for ex: by AccountID, by AccountName, by AccountNumber.
How can we do that so that we don't need a separate HttpGet for each attribute (so we don't have to repeat the same code for different attributes) ?
This is our HttpGet in the AccountsController to get the account by AccountID
public class AccountsController : ControllerBase
{
private readonly IAccountRepository _accountRepository;
[HttpGet("{AccountID}")]
public Account GetAccount(int AccountID)
{
var result = _accountRepository.GetAccount(AccountID);
return result;
}
This is the code in the AccountRepository.cs
public Account GetAccount(int accountID)
{
var result = _db.Query("MyAccountTable").Where("AccountID", accountID).FirstOrDefault<Account>();
return result;
}
This is the Account class
namespace MyApi.Models
{
public class Account
{
public string AccountID { get; set; }
public string AccountName { get; set; }
public string AccountNumber { get; set; }
// other attributes
}
}
Thank you.
Doing it with GET can be a pain, there are ways to pass on the path/query arrays and complex objects but are ugly, the best you can do is to use POST instead of GET and pass an object with the filters that you want.
//In the controller...
[HttpPost]
public Account GetAccount([FromBody]Filter[] DesiredFilters)
{
var result = _accountRepository.GetAccount(DesiredFilters);
return result;
}
//Somewhere else, in a shared model...
public class Filter
{
public string PropertyName { get; set; }
public string Value { get; set; }
}
//In the repository...
public Account GetAccount(Filter[] Filters)
{
var query = _db.Query("MyAccountTable");
foreach(var filter in Filters)
query = query.Where(filter.PropertyName, filter.Value);
return query.FirstOrDefault<Account>();
}
Now you can send a JSON array on the request body with any filters that you want, per example:
[
{ "PropertyName": "AccountID", "Value": "3" },
{ "PropertyName": "AccountName", "Value": "Whatever" }
]

ASP.NET Core Web API Bad Request

I'm a tad confused - I've created an ASP.NET Core Web API MVC Project but when i try to make a request i am getting the following response:
I am posting to https://localhost:44337/api/Solve with Postman the following body:
{
"examId":"0f537776-1acf-478f-82ee-c8476bc3e005",
"selectedAnswers":
[
{
"id":"9163fd1c-ec0f-4f1f-8ead-05ffeac36426",
"answerText":"Yes",
"isCorrect":true
},
{
"id":"00545a13-212b-46a5-9d06-3f6abbb9f1d8",
"answerText":"Yes",
"isCorrect":true
}
]
}
and receive this as a response:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Bad Request",
"status": 400,
"traceId": "8000005f-0001-ff00-b63f-84710c7967bb"
}
I've already included the Content-Type.
GlobalConstants.RouteConstants.ApiRoute = "api/" GlobalConstants.RouteConstants.PostSolve = "Solve"
Here is my controller:
[Route(GlobalConstants.RouteConstants.ApiRoute)]
[ApiController]
public class ESchoolController : ControllerBase
{
protected IApiService ApiService { get; set; }
public ESchoolController(IApiService apiService)
{
this.ApiService = apiService;
}
//POST: api/Solve
[HttpPost]
[Route(GlobalConstants.RouteConstants.PostSolve)]
public IActionResult Solve([FromBody]ExamApiSolveInputModel model)
{
if (!this.ModelState.IsValid)
{
return this.BadRequest();
}
try
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var result = this.ApiService.SolveExam(model, userId);
return this.Ok(result);
}
catch (Exception e)
{
return this.BadRequest(e.Message);
}
}
}
Here are my input models:
public class ExamApiSolveInputModel
{
public ExamApiSolveInputModel()
{
this.SelectedAnswers = new List<AnswerApiInputModel>();
}
public string ExamId { get; set; }
public ICollection<AnswerApiInputModel> SelectedAnswers { get; set; }
}
public class AnswerApiInputModel
{
public string Id { get; set; }
public string AnswerText { get; set; }
public bool IsCorrect { get; set; }
}
I've been searching for solution, but unsuccessfully. I've tried some things like:
When i make request it's not entering the controller. I've checked
it with the debugger.
Making another post method but again it's not entering the code via
debugger, so I think the problem isn't in the code.
Any ideas how to solve this problem? Thanks alot and happy holidays!
I removed the services.AddMvc(options => services.AddMvc(options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())); from the Startup.cs file and the problem disappeared!
You need to provide more information how do you make your request.
Make sure you do include your JSON in body and set Content-Type header as "application/json"

Return JSON from API method

I am using ASP.net Core web api (c#) here
I have a JSON string as:
{
"userId":321,
"account":"new
"fname":"Adam",
"lname":"Silver"
"features":[
{
"available":true,
"status":open,
"admin":false
}
]
}
I want to test this data in my angular code so wanted to hardcode this into my API; then I want my API to return this back. What I am finding it hard is how to return this. Shall I return this as a string or need to parse it?
I have this method in my API:
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
Do I need to represent this into string or parse it someway?
Your JSON is invalid. We need to correct it. JSONLint can be helpful for that. I took your JSON and corrected the syntax errors until I got this:
{
"userId": 321,
"account": "new",
"fname": "Adam",
"lname": "Silver",
"features":[
{
"available": true,
"status": "open",
"admin": false
}
]
}
Then I need to generate a C# class structure to represent this JSON. I could manually create it, but the excellent json2csharp.com can generate it for me quickly. I fed this JSON into and received the following classes back:
public class Feature
{
public bool available { get; set; }
public string status { get; set; }
public bool admin { get; set; }
}
public class RootObject
{
public int userId { get; set; }
public string account { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public List<Feature> features { get; set; }
}
I put these class definitions into my application. Then I need to modify my action method to create an instance of this RootObject class (you should change the name to actually match what it's intended for).
[HttpGet]
public ActionResult<RootObject> Get()
{
// Create an instance of our RootObject and set the properties
var myRootObject = new RootObject();
myRootObject.userId = 321;
myRootObject.account = "new";
myRootObject.fname = "Adam";
myRootObject.lname = "Silver";
myRootObject.features = new List<Feature>();
// Create an instance of a feature and set its properties
var feature = new Feature();
feature.available = true;
feature.status = "open";
feature.admin = false;
// Add the new feature to the features collection of our RootObject
myRootObject.features.Add(feature);
// Return the instance of our RootObject
// The framework will handle serializing it to JSON for us
return myRootObject;
}
Note that I changed the signature of your method. I made it no longer accept an IEnumerable because it wasn't clear why you had that. And I changed it to return an ActionResult after checking Microsoft's documentation.
Hi Please find correct JSON format for above one:
{
"userId": 321,
"account": "new",
"fname": "Adam",
"lname": "Silver",
"features": [{
"available": true,
"status": "open",
"admin": false
}]
}
you can use below class in your web API to pass respective data
public class Feature
{
public bool available { get; set; }
public string status { get; set; }
public bool admin { get; set; }
}
public class RootObject
{
public int userId { get; set; }
public string account { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public List<Feature> features { get; set; }
}
then at the end, while returning data, convert the respective class object into JSON by serializing that into JSON format.
Hope it will fulfill your requirement.
Putting the comments into an answer:
If you are using ActionResult, I'll assume you are using asp.net mvc. What you want is JsonResult.
[HttpGet]
public JsonResult Get()
{
return new JsonResult
{
Data = new
{
userId = 321,
account = new
{
fname = "Adam",
lname = "Silver",
features = new object[]{
new
{
available = true,
status = "open",
admin = false
}
}
}
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}

C# / MVC Validate model data and call external API based on the results

I have created an WebAPI web app and I would like to validate the data when POST and based on the results to call an external API.
The data will be saved in the database as it is, apart from the validation results.
Validation will be done only for calling the external API.
I have created the logic for posting to the external API but I'm not quite sure how it will be the optimal way to validate the data.
My model includes 10 classes like the below Class1 with multiple properties and I've created a controller for each of them.
The properties can have the true/false values but as strings.
public class Class1
{
public ICollection<Class1Data> Data { get; set; }
}
public class Class1Data
{
public int Id { get; set; }
public string Prop1{ get; set; }
public string Prop2 { get; set; }
..
public string Prop10 { get; set; }
}
WebAPI contoller for POST:
[ResponseType(typeof(Class1))]
public async Task<IHttpActionResult> PostClass1(Class1 class1)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Class1.Add(class1);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = Class1.Id }, class1);
}
I've manage somehow to validate one property and POST to external API but not quite sure how I can do that for all my model classes ( I have around 10, 20 props each ).
var notValid = Class1.Data.Where(x => x.Prop1 == "False");
if (notValid != null)
{
foreach ( var fault in notValid )
{
// Call external API using fault.Prop1 / fault.Prop5 / ..
}
}
How could I achieve this?
I hope that my question makes any sense to you.
The simplest way is to use Data Annotations:
Examples:
[StringLength(100)]
public string AccountKey { get; set; }
[Required]
[StringLength(100)]
public string FirstName { get; set; }
Or if you need custom validations you can define them as Custom Validation Attributes and use them like below:
[Required]
[CountryCode]
[StringLength(3)]
public string CountryCode { get; set; }
In this sample [CountryCode] is a Custom validation which you can implement like this:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class CountryCodeAttribute : RegularExpressionAttribute
{
public CountryCodeAttribute() :
base("^[A-z]{2,3}([-]{1}[A-z]{2,})?([-]?[A-z]{2})?$")
{
ErrorMessage = "Invalid country code.";
}
}
You will need to import this namespace for this kind of validation:
System.ComponentModel.DataAnnotations

Getting null value from Web api

I am trying to store a List of strings that are members of a conversation.
[DataContract]
public class Conversation
{
[Key]
[DataMember]
public string Key { get; set; }
[DataMember]
public string ConversationName { get; set; }
[DataMember]
public string Administrator { get; set; }
[DataMember]
public List<string> Members { get; set; }
public Conversation(string key, string name, string admin, List<string> members)
{
Key = key;
ConversationName = name;
Administrator = admin;
Members = members;
}
public Conversation()
{
}
}
I am using Postman to make a Post request to the URI which seems to work fine, returning 201 Created and giving a json object containing the correct info.
This is the json I am posting:
{
"Key": "123",
"ConversationName": "Test",
"Administrator": "Ken",
"Members": ["y#12.com", "f#78.com"]
}
However, when I try to get the conversation in a GET method this is the result:
{
"Key": "123",
"ConversationName": "Test",
"Administrator": "Ken",
"Members": null
}
These are my functions in my controller:
Post:
[HttpPost]
//[ResponseType(typeof(Conversation))]
public async Task<IHttpActionResult> PostConversation(Conversation convo)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Conversations.Add(convo);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { name = convo.Key }, convo);
}
GET:
[HttpGet]
public IQueryable<Conversation> GetConversations()
{
return db.Conversations;
}
Any help would be much appreciated. Thanks!
The post basically returns the entity as you offer it, which is with Members, apparently.
The get loads a Conversation from the database. It can't have any Members at that moment, because Members is a List<string>, which can't possibly be mapped to a database column.
You probably expect the post to store the Members into the database. This doesn't happen, because you should use Member entities in stead of strings. Member can be a simple class having an Id and a string property.

Categories