Debugger Stops while Parsing Rest API response HttpClient, Xamarin Forms - xamarin.forms

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

Send data as multipart/form-data

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
}

Troubleshooting model binding problem in ASP.NET Core 3.1 API

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.

How to Assert for Response Code for request using flurl

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>();

IdentityUser: "Name cannot be null or empty"

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

HTTP POST to Many to Many relation using ASP.NET Web API

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);
}
}

Resources