0

I am developing an application with Angular 12 in the frontend and ASP.net core 5 in API. When calling HttpPost API I get an error Failed to load resource: the server responded with a status of 400 ()

If I use string or integer parameters there is no issue and API is called properly. But only when I am passing values with entity class, I get this error.

Here is my entity class in asp.net core

public class FrontEnd
{ 
  public class SendOTPReq
    {
        public string MobileNo { get; set; }
        public string Email { get; set; }
        public int Type { get; set; }
    }
}

And here is the API

    [Route("SendOTP")]
    [HttpPost]
    public async Task<object> SendOTP(SendOTPReq otpReq)
    {
        try
        {
            CommonApiResponse commonResponse = new CommonApiResponse();

            if (!ModelState.IsValid)
            {
                return new APIResponse(406, "", ModelState.Select(t => new { t.Key, t.Value.Errors[0].ErrorMessage }));
            }
            var Result = await _IFrontEndRepository.SendOTP(otpReq);
            if (Result != null)
            {
                commonResponse.Status = "Success";
                commonResponse.ErrorMsg = "";
                commonResponse.ResultData = Result;
                return new APIResponse(200, "Request Success", commonResponse);
            }
            else
            {
                commonResponse.Status = "Failed";
                commonResponse.ErrorMsg = Result.Message;
                commonResponse.ResultData = Result;
                return new APIResponse(204, "Request Failed", commonResponse);
            }
        }
        catch (Exception e)
        {

            throw new Exception(e.Message);
        }

    }

Here is my angular component.ts code:

const otpReq = new SendOTPReq();

otpReq.MobileNo = this.registerForm.controls['phoneNo'].value;
otpReq.Email = this.registerForm.controls['email'].value;
otpReq.Type = 2; //2 is or email and 1 is for sms

this.accountsService.sendOTP(otpReq).subscribe(      
  (data) => {
         //do stuff here
    
         });

Angular service method:

export class AccountsService {

constructor(private httpClient: HttpClient,
private _router: Router) { }

 sendOTP(otpReq: SendOTPReq): Observable<SendOTPReq> {
  debugger;
  return this.httpClient.post<SendOTPReq>(environment.ApiUrl + 'FrontEnd/SendOTP', otpReq)
    .pipe(
      catchError(this.errorHandler),
    );
}


errorHandler(error: { error: { message: string; }; status: undefined; message: any; }) {
  debugger;
  let errorMessage = '';
  if (error.error instanceof ErrorEvent) {
    // Get client-side error
    errorMessage = error.error.message;
  } else {
    // Get server-side error
    if(error.status == undefined)
    {
      errorMessage = "The server connection with the application is showing error. Close the application and restart again."
    }else{
    errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
  }      
  return throwError(errorMessage);
}

}

Angular model class:

export class SendOTPReq
{
  MobileNo!:string;
  Email!: string;
  Type!: number;
}

What could be going wrong?

user1181942
  • 1,587
  • 7
  • 35
  • 45

3 Answers3

1

The return type of your POST request is wrong. You can't have your controller return object when your Angular httpClient asks to have a SendOTPReq returned.

So instead you need

return this.httpClient.post<MyActualReturnType>(environment.ApiUrl + 'FrontEnd/SendOTP', otpReq)
Bertramp
  • 376
  • 1
  • 15
  • Thank you for your response. I thought what a silly mistake I did, but even after correction, it's not working. – user1181942 Jun 21 '21 at 12:36
  • Hmm.. i can see you're also not adding observe: 'body' to the request options. That mistake will just result in a client side error and not a 400 response to my knowledge though. You can try it out – Bertramp Jun 21 '21 at 13:19
  • tried.. not working.. do i need to change anything on API end? – user1181942 Jun 21 '21 at 14:40
1

Try adding a [FromBody] decorator in your API like this:

public async Task<object> SendOTP([FromBody]SendOTPReq otpReq)

UPDATE: Based on comments on this and other answers, it seems like setting up some error handling as well as being a little more verbose in your POST request may shed some more light into what is causing the issue here. Assuming you have the [FromBody] decorator as I've described above in your API, let's further enhance the API focusing on return types, and the service function in Angular using RxJs to see if we can somehow extract a more helpful response out of this.

For clarity and to summarize, un-nest your SendOtpReq class:

public class SendOTPReq
{
    public string MobileNo { get; set; }
    public string Email { get; set; }
    public int Type { get; set; }
}

Next, let's adjust the return type of your controller POST method to IActionResult:

[Route("SendOTP")]
[HttpPost]
public async Task<IActionResult> SendOTP([FromBody]SendOTPReq otpReq)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    try
    {                
        var Result = await _IFrontEndRepository.SendOTP(otpReq);
        if (Result != null)
        {
            return Ok(Result);                       
        }

        return BadRequest($"Fail to POST");    
     }
     catch (Exception ex)
     {    
         return BadRequest($"Fail to POST");
     }

 }

Now, let's observe the entire response from the API using your angular Service, instead of just the body:

public sendOTP(otpReq: SendOTPReq): Observable<HttpResponse<SendOTPReq>> {
  return this.httpClient.post<SendOTPReq>(environment.ApiUrl +
             'FrontEnd/SendOTP', otpReq { observe: "response" })
             .pipe(
             catchError(this.errorHandler),
    );
}
cklimowski
  • 589
  • 6
  • 21
1

So, finally, I fixed it. Commented properties and uncommented one after another. When I was fetching data from 'mobileNo' field, I was not converting it to string. Converting mobile number to string worked.

I changed the line:

otpReq.MobileNo = this.registerForm.controls['phoneNo'].value;

to

otpReq.MobileNo = this.registerForm.controls['phoneNo'].value.toString();
user1181942
  • 1,587
  • 7
  • 35
  • 45