I am trying to parse response from ASP.NET Core Web API. I am able to parse the response JSON into C# object successfully but app crashes without throwing any error when the parsed C# object is returned to the ViewModel.
in ViewModel
ApiResponse response = await _apiManager.GetAsync<ApiResponse>("authentication/GetUserById/1");
Response JSON:
{
"result": {
"id": 1,
"userType": 1,
"firstName": “FirstName”,
"middleName": null,
"lastName": “LastName”,
},
"httpStatusCode": 200,
"httpStatusDescription": "200OkResponse",
"success": true,
"message": "hello"
}
HttpClient GetAsync() method:
public async Task<TResult> GetAsync<TResult>(string endpoint)
{
HttpResponseMessage httpResponse = _httpClient.GetAsync(endpoint).GetAwaiter().GetResult();
httpResponse.EnsureSuccessStatusCode();
TResult t = default(TResult);
if (httpResponse.IsSuccessStatusCode)
{
string serialized = await httpResponse.Content.ReadAsStringAsync();
t = JsonConvert.DeserializeObject<TResult>(serialized);
}
return t;
}
App crashes (debugger stops without any error) at "return t" statement. Here, _httpClient is a singleton object of HttpClient using DI.
TResult model is ApiResponse object
public class User
{
[JsonProperty("id")]
public int UserId { get; set; }
[JsonProperty("userType")]
public int UserType { get; set; }
[JsonProperty("firstName")]
public string FirstName { get; set; }
[JsonProperty("middleName")]
public string MiddleName { get; set; }
[JsonProperty("lastName")]
public string LastName { get; set; }
}
public abstract class ResponseBase
{
[JsonProperty("httpStatusCode")]
public int HttpStatusCode { get; protected set; }
[JsonProperty("httpStatusDescription")]
public string HttpStatusDescription { get; protected set; }
[JsonProperty("success")]
public bool Success { get; protected set; }
[JsonProperty("message")]
public string Message { get; protected set; }
}
public class ApiResponse : ResponseBase
{
[JsonProperty("result")]
public User Result { get; set; } = new User();
}
There are two issues:
1. when the following statement executes, app crashes and debugger stops without throwing any error.
HttpResponseMessage httpResponse = await _httpClient.GetAsync(endpoint).ConfigureAwait(false);
But when GetAsync() is called with .GetAwaiter().GetResult(), network call is placed successfully. I do not understand why ConfigureAwait(false) fails.
HttpResponseMessage httpResponse = _httpClient.GetAsync(endpoint).GetAwaiter().GetResult();
why the following call fails and app crashes? How can I return parsed C# object to the calling code?
return JsonConvert.DeserializeObject(serialized);
Please advise.
Try this
try
{
var result = await httpClient.GetAsync(endpoint);
var response = await result.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<TResult>(response);
}
catch (Exception exp)
{
Console.Write(exp.InnerMessage);
}
Make sure that you have installed Newtonsoft.Json
Related
I have .NET Core back end, that receive DTO as form-data.
Here is controller
[HttpPost]
public async Task<IActionResult> Register([FromForm] RegisterDto model)
{
var result = await _authAppService.Register(model);
if (result.Code == 409)
{
return BadRequest();
}
return Ok(result.Token);
}
Here is DTO
public class RegisterDto
{
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string PasswordConfirmation { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string GcmToken { get; set; }
}
I need to send data from Angular app via form-data
I defined model at Angular side
Here is it
export class RegisterDto{
Email: string;
Password: string;
PasswordConfirmation: string;
FirstName: string;
LastName: string;
}
And this is how I try to do form-data sending
register(){
const formData = new FormData();
formData.append(this.registerObject);
}
in append method I have this erroк
Expected 2-3 arguments, but got 1.ts(2554)
How I can send DTO via form data?
You can't append the whole object without giving it a field name. As the error says it acceptes 2-3 arguments however you are only providing one argument without giving it a field name.
register(){
const formData = new FormData();
formData.append('regObj', this.registerObject);
}
however it's good if you append each field as formdata value such as.
formGroup: FormGroup;
formData: FormData;
register() {
this.formData.append('Email', this.formGroup.controls.Email.value);
this.formData.append('Password', this.formGroup.controls.Password.value);
this.formData.append('FirstName', this.formGroup.controls.FirstName.value);
this.formData.append('LastName', this.formGroup.controls.LastName.value);
// call your service and send it
}
I'm trying to send an object via a POST request to my ASP.NET Core 3.1 API but I keep getting Bad Request error. As far as I can see, I do have a class that matches what I'm expecting perfectly but clearly it's not. How can I see exactly what the problem is?
The following fails:
public async Task<IActionResult> Post([FromBody] MyCustomObject input)
{
// Do something here...
}
If I use a dynamic, it works fine. So the following code works fine:
public async Task<IActionResult> Post([FromBody] dynamic input)
{
// Do something here...
}
As I said, I'm just getting a 400, Bad Request error. I've been going over MyCustomObject again and again and it looks identical to the object that I'm sending.
Here's what my custom class looks like:
public class CreateContactVm
{
[GuidEmptyNotAllowed]
public Guid AccountId { get; set; }
[Required]
public string AccountName { get; set; }
[GuidEmptyNotAllowed]
public Guid ContactGroupId { get; set; }
[IntZeroNotAllowed]
public int ContactType { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string EntityName { get; set; }
public List<AddEmailVm> Emails { get; set; } = new List<AddEmailVm>();
public List<AddPhoneNumberVm> PhoneNumbers { get; set; } = new List<AddPhoneNumberVm>();
public List<AddAddressVm> Locations { get; set; } = new List<AddAddressVm>();
}
Here, I use some custom validations such as [GuidEmptyNotAllowed] or [IntZeroNotAllowed]. I inspect the object I send via my POST call and it satisfies ALL of these requirements and yet it still fails.
How can I get more information about why my API method is throwing a 400 error?
UPDATE:
The following code allows me to convert what comes in as a dynamic to my CreateContactVm custom class but I really shouldn't have to do this at all:
CreateContactVm request = new CreateContactVm();
try
{
var element = (JsonElement)input; // input is the dynamic received
request = JsonUtils.Deserialize<CreateContactVm>(element.GetRawText());
}
catch(Exception e)
{
var error = e.Message;
}
This also proves that the issue is with model binding. Something in my custom class is not liking the JSON object it receives.
I was trying to put an assert for Response Code for my request, but i am having hard time to figure out, could you please help me on this. Here is my implementation and definition.
myTests.cs
var accessToken = await helper.SendRequestAsync<AccessToken>(baseUrl, body);
==> how to set assert here right after above statement to verify response status?
helpers.cs
public static async Task<T> SendRequestAsync<T>(string baseUrl, Dictionary<string, string> body)
{
using (var flurl_client = new FlurlClient(baseurl))
{
try
{
var response = await flurl_client
.Request()
.PostUrlEncodedAsync(body)
.ReceiveJson<T>();
return response;
}
catch (Exception ex)
{
Assert.Fail(ex.Message);
}
return default(T);
}
}
======================================================
Data model for "AccessToken" is in Dto.cs
public class AccessToken
{
public string token_type { get; set; }
public string expires_in { get; set; }
public string ext_expires_in { get; set; }
public string expires_on { get; set; }
public string not_before { get; set; }
public string resource { get; set; }
public string access_token { get; set; }
public string refresh_token { get; set; }
public object Status_Code { get; set; }
}
If you're you looking for Flurl's testing features to help with this, I'm afraid it won't work. Those features are specifically designed for testing the behavior of your client-side code based on fake responses that you set up in your test. It looks like you want to assert the status code from a real call.
The best way I can think of is to drop the .ReceiveJson<T>() line in SendRequestAsync and change the method signature to return Task<HttpResponseMessage>:
using System.Net.Http;
public static async Task<HttpResponseMessage> SendRequestAsync(string baseUrl, Dictionary<string, string> body)
{
using (var flurl_client = new FlurlClient(baseurl))
{
try
{
var response = await flurl_client
.Request()
.PostUrlEncodedAsync(body); // this returns Task<HttpResponseMessage>
return response;
}
catch (Exception ex)
{
Assert.Fail(ex.Message);
}
return null;
}
}
Then your test can do this:
var resp = await Helper.SendRequestAsync(...);
Assert.AreEqual(HttpStatusCode.OK, resp.StatusCode);
Anything that needs the deserialized response body can do this:
var token = await Helper.SendRequestAsync(...).ReceiveJson<AccessToken>();
I've been trying to inherit IdentityUser to make my own class which uses Identity and still writes to my database and I keep getting this error when I try to call my registration post method:
{
"$id": "1",
"Message": "The request is invalid.",
"ModelState": {
"$id": "2",
"": [
"Name cannot be null or empty."
]
}
}
I tried number of things, but nothing works.For example when I try to set UserName field of IdentityUser it says it's impossible because it doesn't exist in the context.
The important thing to mention would be that I am using ADO.NET database first model for the account :)
This is the class:
public partial class Account :IdentityUser
{
public Account()
{
this.Families = new HashSet<Family>();
}
public long idAccount { get; set; }
public string email { get; set; }
public string password { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public virtual ICollection<Family> Families { get; set; }
}
This is my authentication repository class:
public class AuthRepository :IDisposable
{
private DAKPAKEntities _ctx;
private UserManager<Account> _userManager;
public AuthRepository()
{
_ctx = new DAKPAKEntities();
_userManager = new UserManager<Account>(new UserStore<Account>(_ctx));
}
public async Task<IdentityResult> RegisterUser(Account userModel)
{
Account user = new Account
{
firstName = userModel.firstName,
lastName = userModel.lastName,
email=userModel.email
};
var result = await _userManager.CreateAsync(user,userModel.password);
return result;
}
}
And this is the controller that calls the repository:
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(Account userModel)
{
IdentityResult result = await _repo.RegisterUser(userModel);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
I am new to this, and am out of options to try. I did almost everything that's usually suggested for this type of error, please help :)
It looks like you haven't done everything that is required in order to change the way ASPNet Identity stores the user information in the database.
Suggest you start here: Overview of Custom Storage Providers for ASP.NET Identity
I am new to ASP.net (and programming in general) and I'm having trouble building a Web API. More specifically I need help in these two areas:
How to configure my DOCcontroller to post a new document (DOC table).
How to make the actual ajax post -- I am having trouble passing the EXT_GUID parameter. As it stands I get an error when I try to post. "Can't bind multiple parameters (doc and parentOwner) to the request's content."
Essentially this is for a simple document management system. I want Get/Post documents (DOC) by having the user supply an GUID from an external database (the EXT_GUID field) as a filter/parameter. Each document can have multiple EXT_GUIDs and each EXT_GUID can have multiple Documents (DOC). You can assume that the EXT_GUID fields we be populated prior to the http post.
This is the DOCcontroller code
//POST api/DOC
public HttpResponseMessage PostDOC(DOC doc, List<string> parentOwners)
{
if (ModelState.IsValid)
{
var parents = db.BIMs.Where(bx => parentOwners.Contains(bx.EXT_GUID));
foreach (var p in parents)
doc.Owners.Add(p);
db.DOCs.Add(doc);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, doc);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = doc.Id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
This is my model setup -- EntityFramework codefirst stuff
public class EXT
{
public int Id { get; set; }
public string EXT_GUID { get; set; }
public int ProjectID { get; set; }
public virtual ICollection<DOC> DOCs { get; set; }
}
public class DOC
{
public int Id { get; set; }
public int ProjectID { get; set; }
public string Subject { get; set; }
public string Link { get; set; }
public virtual ICollection<EXT> EXTs { get; set; }
}
This is more Storage Model...
public StoreDBContext() : base("name=StoreDBContext")
{
}
public DbSet<EXT> EXTs { get; set; }
public DbSet<DOC> DOCs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Set FLUENT API config for many to many here
modelBuilder.Entity<EXT>()
.HasMany(a => a.DOCs)
.WithMany()
.Map(x =>
{
x.MapLeftKey("EXT_Id");
x.MapRightKey("DOC_Id");
x.ToTable("EXTsDOCs");
});
}
AJAX Code
function AddDOC() {
var parentOwner = "{\"" + $('#txtaddEXT').val() + "\"}";
jQuery.support.cors = true;
var DOC = {
ProjectId: ProjectID,
Subject: $('#txtaddDOCSubject').val(),
Link: $('#txtaddDOCLink').val(),
parentOwner: parentOwner
};
$.ajax({
url: "http://localhost:54171/api/DOC/",
type: 'POST',
data: JSON.stringify(DOC),
contentType: "application/json;charset=utf-8",
success: function (data) {
WriteResponse(data);
},
error: function (x, y, z) {
alert(x + '\n' + y + '\n' + z);
}
});
}
What you receive from the client and what you will save in the database is two different things.
Your doc object is ok:
var DOC = {
ProjectId: ProjectID,
Subject: $('#txtaddDOCSubject').val(),
Link: $('#txtaddDOCLink').val(),
parentOwner: parentOwner
};
Now you need to change the server logic. Make a model like this:
public class DocReceivedModel
{
public int ProjectID { get; set; }
public string Subject { get; set; }
public string Link { get; set; }
public List<string> parentOwner { get; set; }
}
Then your PostDOC method will be:
public HttpResponseMessage PostDOC(DocReceivedModel docReceived)
{
if (ModelState.IsValid)
{
Doc newDoc = new Doc();
newDoc.ProjectID = docReceived.ProjectID
newDoc.Subject = docReceived.Subject
newDoc.Link = docReceived.Link
var parents = db.BIMs.Where(bx => docReceived.parentOwners.Contains(bx.EXT_GUID));
foreach (var p in parents)
newDoc.Owners.Add(p);
// I not see in your model Owners, maybe this is EXTs but I suppose you catch the idea
db.DOCs.Add(newDoc);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, newDoc);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new {id = newDoc.Id}));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}