How to fix validation errors Response in Netcore 2.2 Web API - asp.net-core-2.2

I'm handling asp.netcore-2.2 errors but I'm still getting ErrorMessage details from Response
I have a login and register methods with a required username, password and a required password length for registration between 8 and 4 characters the problem is that I get the error messages expected or handled for the login method and I get validation error when I register with empty username and password.
I added this code in ConfigureApiBehaviorOptions:
options.SuppressModelStateInvalidFilter = true;
and this I tried this option too:
options.SuppressUseValidationProblemDetailsForInvalidModelStateResponses = true;
but it doesn't work.
This is my Register DTO:
using System.ComponentModel.DataAnnotations;
namespace DatingApp.Api.Dtos
{
public class UserForRegisterDto
{
[Required]
public string Username { get; set; }
[Required]
[StringLength(8, MinimumLength= 4, ErrorMessage= "password between 4 and 8 charracters")]
public string Password { get; set; }
}
}
I expected this error message:
"password between 4 and 8 charracters"
but I get this before I change ConfigureApiBehaviorOptions:
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "0HLJIO56EGJEV:00000001"
and a full table with error detail if I use ConfigureApiBehaviorOptions.

public class UserForRegisterDto
{
[Required(ErrorMessage = "username is required")]
public string Username { get; set; }
[Required(ErrorMessage = "password is required")]
[StringLength(8, MinimumLength= 4, ErrorMessage= "password between 4 and 8 charracters")]
public string Password { get; set; }
}
i just add error message for required fields .

Related

Validation Annotation - Entity Framework Core

I have a model which is posted to my backend from a cshtml view. This model is also a representation of a Database-Table.
This code looks for the HTTP POST like the following:
[HttpPost]
public IActionResult CreateAKG(Conversation akg)
{
if (ModelState.IsValid && ValideD3OrD4Values(akg))
{
akg.RDPflichfelderBefuellt = true;
}
else
{
akg.RDPflichfelderBefuellt = false;
}
if (akg.Kommentare == null)
{
akg.Kommentare = new List<Kommentar>();
}
if (akg.AuftragsklaerungsgespraechId == 0)
{
this.MyDatabase.Conversation.Add(akg);
}
else
{
this.MyDatabase.Conversation.Update(akg);
}
this.MyDatabase.SaveChanges();
return RedirectToAction("Index");
}
The class which represents the model is called Conversation. There are some properties which are annotated by Validation Annotations. The annotation should only be used by the Controller / ModelState.IsValid and not for the Database-Table.
Here is the code sample:
public class Conversation
{
public int ConversationId { get; set; }
[Required(ErrorMessage = "This field is required.")]
public DateTime? DatumAKG { get; set; }
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungFertigungskosten { get; set; }
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungQVP { get; set; }
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungLayoutkosten { get; set; }
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungBeschaffung { get; set; }
public bool RDPflichfelderBefuellt { get; set; }
}
The ModelState.IsValid is only used to validate if a boolean is true or false. Other validation is not needed.
My problem now is, that in reason of the data-annotation a string which could normally be NULL in a Database is now in the database-design configured as NOT NULL.
If i try to store a new conversation in the database, a error is throw that some string values could not be null.
What i want to do is:
Validation on Controller
No Validation Annotation which changes the Database-Design
Since properties and requirements are different you should have 2 different objects
// this is you database object
public class Conversation {
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungQVP { get; set; }
}
// this your Data Transfer Object
public class ConversationDTO {
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
[Required]
public string KontierungQVP { get; set; }
}
Your EF models should always represent the database. If you want to add extra validations or conditions you should do it with another object and simply transfer between those objects. Having 2 different types of objects gives you more modularity.
EDIT:
There is a way to do what you ask but it's not a recommended approach and it might cause issues. You want to use 2 contexts and configure the required properties using FluentAPI and not data annotations.
// Call this method in your context.
protected override void OnModelCreating(your_builder){
modelBuilder.Entity<Conversation>()
.Property(p => p.KontierungQVP)
.IsRequired();
}
Generally you want to have 2 different contexts. One where you will initialize your database. And another one where you will have your required attributes defined by the FluentAPI (not data annotations).
So to recap one DbContext for your DB creation and one other for your operations. Of course this will lead to disparencies and it is easy to forget a validation on the database and so on.

Display Information to the UI

So what I have been trying is this: After successful registration the user gets a Message in the interface to show that Registration was successful. My first method was I declared a Message variable and use Data Binding to bind the result to a label in my RegisterPage.xaml. That failed because whether the message is successful or not the label is not showing. So I commented out using a label and tried DisplayAlert but DisplayAlert is giving an error- does not exist in the current context.
Please help, still learning.
public class RegisterViewModel
{
private readonly DataService dataService = new DataService();
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public string Message { get; set; }
public ICommand RegisterCommand
{
get
{
return new Command(async () =>
{
var isRegistered = await dataService.RegisterUserAsync(Email, Password, ConfirmPassword);
Settings.Username = Email;
Settings.Password = Password;
if (isRegistered)
{
//DisplayAlert( "Alert" , "Registered", "OK");
//Message = "Registered Successfully :)";
// DependencyService.Get<Toast>().Show("You have registered succefully");
Application.Current.MainPage = new NavigationPage(new EntryPage());
}
else
{
Message = " Retry Later :(";
}
});
}
}
}
DisplayAlert is part of the Page class. If you want to display an alert from a view model (there are many results on Google), you'd call a method like:
private async Task DisplayGenericDialog(string title, string message)
{
await App.Current.MainPage.DisplayAlert(title, message, "OK");
}

ASP.net REST service with MS SQL base serializing list as null

Running a ASP.net REST service using a MS SQL server as base.
Model
public class Bookings
{
[Key]
public int BookingId { get; set; }
[Required]
public int UserID { get; set; }
public List<string> BookISBN { get; set; }
}
Sample generated documentation showing the format as "correct"
Sending following data over postman
{
"BookingId": 1,
"UserID": 2,
"BookISBN": [
"sample string 1",
"sample string 2",
"sample string 3"
]
}
However the last list "BookISBN" column is not generated, and API returns parameter as NULL / i:nil="true"
<ArrayOfBookings xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/PublisherDataModel">
<Bookings>
<BookISBN xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" i:nil="true"/>
<BookingId>1</BookingId>
<UserID>2</UserID>
</Bookings>
<Bookings>
<BookISBN xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" i:nil="true"/>
<BookingId>2</BookingId>
<UserID>2</UserID>
</Bookings>
</ArrayOfBookings>
POST controller for Bookings.
// POST api/Bookings
[ResponseType(typeof(Bookings))]
public IHttpActionResult PostBooking(Bookings Booking)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Bookings.Add(Booking);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = Booking.BookingId }, Booking);
}
What am i missing?
You should decorate your action with HttpPost;
[HttpPost]
[ResponseType(typeof(Bookings))]
public IHttpActionResult PostBooking(Bookings Booking)
I had to include / reference it specifically
db.Entry(Booking).Reference(b => b.BookISBNList).Load();

Remote validation restrict for edit controller method

I have model for register
my model class
public class RegisterViewModel
{
[Required]
[StringLength(100, ErrorMessage = "Please enter a minimum of {2} characters", MinimumLength = 6)]
[DisplayName("University ID")]
[Remote("doesusernameExist", "HEC",null, ErrorMessage = "usr name is allready exist", HttpMethod = "POST")]
public string usrname { get; set; } }
my json controller class
[HttpPost]
public JsonResult doesusernameExist(string usrname)
{
var institute = db.Institutes.Find(HEI_ID);
return Json(institute == null);
}
for create new user and edit user I'm using above model . so without create another model , I want to disable doesusernameExist calling method in edit method
First Add a Hidden field in View as:
#Html.hidden("PreviousUsername", Model.usrname)
In your .cs class add following:
[Remote("doesusernameExist", "HEC", ErrorMessage = "usr name is allready exist", AdditionalFields = "PreviousUsername")]
public string usrname { get; set; }
An your controller Action Method should be like:
public JsonResult doesusernameExist(string usrname, string PreviousUsername)
{
if(usrname==PreviousUsername)
{
return true;
}
var institute = db.Institutes.Find(HEI_ID);
return Json(false, JsonAlloBehaviour.AllowGet);
}
First in Edit View disable client side validation for username:
#Html.TextBoxFor(m => m.username, new { #data_val = "false" })
Second in Edit Post Action remove validation result for username from ModelState:
public ActionResult EditUser([Bind(Exclude = "usrname")]RegisterViewModel model)
{
ModelState.Remove("username");
if (ModelState.IsValid)
{
.
.
.

WebAPI Serialization problems when consuming Recurly Rest API which returns XML

I'm new to the ASP.Net Web API. I'm trying to interact with the Recurly REST based API and I am getting errors like below during my ReadAsAsync call which is the point I believe it attempts to serialize the response.
{"Error in line 1 position 73. Expecting element 'account' from namespace 'http://schemas.datacontract.org/2004/07/RecurlyWebApi.Recurly'.. Encountered 'Element' with name 'account', namespace ''. "}
Here is my HttpClient implementation, simplified for brevity:
public class RecurlyClient
{
readonly HttpClient client = new HttpClient();
public RecurlyClient()
{
var config = (RecurlySection)ConfigurationManager.GetSection("recurly");
client.BaseAddress = new Uri(string.Format("https://{0}.recurly.com/v2/", config.Subdomain));
// Add the authentication header
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(config.ApiKey)));
// Add an Accept header for XML format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
}
public T Get<T>(string id)
{
var accounts = default(T);
// Make the request and get the response from the service
HttpResponseMessage response = client.GetAsync(string.Concat("accounts/", id)).Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
accounts = response.Content.ReadAsAsync<T>().Result;
}
return accounts;
}
}
And here is my model:
[XmlRoot("account")]
public class Account
{
[XmlAttribute("href")]
public string Href { get; set; }
[XmlElement("account_code")]
public string AccountCode { get; set; }
[XmlElement("state")]
public AccountState State { get; set; }
[XmlElement("username")]
public string Username { get; set; }
[XmlElement("email")]
public string Email { get; set; }
[XmlElement("first_name")]
public string FirstName { get; set; }
[XmlElement("last_name")]
public string LastName { get; set; }
[XmlElement("company_name")]
public string Company { get; set; }
[XmlElement("accept_language")]
public string LanguageCode { get; set; }
[XmlElement("hosted_login_token")]
public string HostedLoginToken { get; set; }
[XmlElement("created_at")]
public DateTime CreatedDate { get; set; }
[XmlElement("address")]
public Address Address { get; set; }
}
And an example of the XML response from the service:
<account href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01">
<adjustments href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01/adjustments"/>
<invoices href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01/invoices"/>
<subscriptions href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01/subscriptions"/>
<transactions href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01/transactions"/>
<account_code>SDTEST01</account_code>
<state>active</state>
<username>myusername</username>
<email>simon#example.co.uk</email>
<first_name>First name</first_name>
<last_name>Last name</last_name>
<company_name>My Company Name</company_name>
<vat_number nil="nil"></vat_number>
<address>
<address1>My Address Line 1/address1>
<address2>My Address Line 2</address2>
<city>My City</city>
<state>My State</state>
<zip>PL7 1AB</zip>
<country>GB</country>
<phone>0123456789</phone>
</address>
<accept_language nil="nil"></accept_language>
<hosted_login_token>***</hosted_login_token>
<created_at type="datetime">2013-08-22T15:58:17Z</created_at>
</account>
I think the problem is because by default the DataContractSerializer is being used to deserialize the XML, and by default the DataContractSerializer uses a namespace of namespace http://schemas.datacontract.org/2004/07/Clr.Namespace. (In this case Clr.Namepace is RecurlyWebApi.Recurly.)
Because your XML has attributes, you need to use the XmlSerializer instead of the DataContractSerializer, and you're set up to do this because your account class is decorated with Xml* attributes. However, you have to use an XmlMediaTypeFormatter which is using the XmlSerializer. You can do this by setting a flag on the global XMLFormatter as described on this page:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
or by supplying a MediaTypeFormatter as a parameter to your ReadAsAsync call:
var xmlFormatter = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xmlFormatter.UseXmlSerializer = true;
accounts = response.ReadAsAsync<T>(xmlFormatter).Result
Not 100% sure of this because this doesn't explain why the first 'account' in your error message is lower case - the DataContractSerializer should ignore the XmlRoot attribute.

Resources