2

I have the below code to make an HTTP request to an external endpoint, which throws me a 422 status code which is Unprocessable Entity. The same request payload works fine when directly invoked the external URL using Postman.

using (HttpClient httpClient = new HttpClient())
{
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var Json = JsonConvert.SerializeObject(loanRequest, new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver
                {
                    IgnoreSerializableAttribute = false
                }
            });
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "externalEndPointURL");

            request.Content = new StringContent(Json,Encoding.UTF8,"application/json");//CONTENT-TYPE header
            HttpContent httpContent = new StringContent(Json, Encoding.UTF8, "application/json");
            httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/json; charset=utf-8");
            httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
            await httpClient.SendAsync(request).ContinueWith(task => {

                Console.WriteLine($"Response {task.Result}");
            });
}

//Request Pay load

{
    "Merchant": "GAP",
    "Lender": "BEN",
    "RateType": "VAR",
    "RepaymentType": "PI",
    "PropertyUsage": "INV",
    "CustomerRate": 0.0429,
    "LoanTerm": 20,
    "BorrowingAmount": 600000,
    "RateTerm": null
}

EDIT

Below is the comparison of the two request headers.

The working one Error

Lakshitha
  • 128
  • 1
  • 13

2 Answers2

1

try this, if it is not working, nothing will be working

var baseUri= @"http://localhost:554";
var api = "/api/..";
    
using HttpClient client = new HttpClient { BaseAddress = new Uri(baseUri) };

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    
var json = JsonConvert.SerializeObject(loanRequest);    
    
var content = new StringContent(json, UTF8Encoding.UTF8, "application/json");

var response = await client.PostAsync(uri, content);

if (response.IsSuccessStatusCode)
{
    var stringData = await response.Content.ReadAsStringAsync();
    var result = JsonConvert.DeserializeObject<object>(stringData);
}

UPDATE

Json you posted has

"PropertyUsage": "INV",

but LoanRequestDTO doesn' t have this property. Since you are using an API Controller it automatically validates input and return an error immediatly, without trigering the action. I am wondering how this extra property could be created during serialization. Maybe you don't have some more properties ?

Serge
  • 40,935
  • 4
  • 18
  • 45
  • Sorry, it does not and is still the same. IsSuccessStatusCode of the response is false. – Lakshitha Jan 08 '22 at 23:28
  • @Lakshitha can you post loanRequest object too, pls? And the API pls – Serge Jan 08 '22 at 23:33
  • This is request object { "Merchant": "GAP", "Lender": "BEN", "RateType": "VAR", "RepaymentType": "PI", "PropertyUsage": "INV", "CustomerRate": 0.0429, "LoanTerm": 20, "BorrowingAmount": 600000, "RateTerm": null } – Lakshitha Jan 08 '22 at 23:34
  • I can see it in you post. How you get it? And I need to see an API since it returns not success code. – Serge Jan 08 '22 at 23:35
  • Pass it via the Request Body as follows. ` [HttpPost] [Route("loan-save-amount")] public async Task RetrieveLoanSaveAmount([FromBody] LoanRequestDTO loanRequest) {} ` – Lakshitha Jan 08 '22 at 23:37
  • Return type is just set to string for the debugging puposes. – Lakshitha Jan 08 '22 at 23:38
  • What about LoanRequestDTO class? – Serge Jan 08 '22 at 23:39
  • It goes as follows. `` public class LoanRequestDTO { public string Merchant { get; set; } public string Lender { get; set; } public string RateType { get; set; } public string RepaymentType { get; set; } public decimal CustomerRate { get; set; } public int LoanTerm { get; set; } public decimal BorrowingAmount { get; set; } public string RateTerm { get; set; } } ``` – Lakshitha Jan 08 '22 at 23:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/240875/discussion-between-lakshitha-and-serge). – Lakshitha Jan 08 '22 at 23:40
  • Just found it and the reason was missing property from my request. Thank you @Serge !! – Lakshitha Jan 08 '22 at 23:48
0

Check the headers postman sends (either by clicking "hidden" button in the headers tab for request or in the postman console after sending the request). Try adding ones which are missing from your request. Quite often sites are pretty nitpicky about User-Agent so I would start from that one first:

httpClient.DefaultRequestHeaders
    .TryAddWithoutValidation(HeaderNames.UserAgent,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")

P.S.

Also common consensus is that it is better to reuse HttpClient, so unless this is one off call - try to share the client between requests.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • That's a good suggestion. But both headers look the same except for the hostname and the token. But the content type of the response header is different. For the working scenario, it is set as "application/json" whereas in other, "text/plain". Not sure we can modify the content type of the response. And this is a single call so not reusing the HttpClient. – Lakshitha Jan 08 '22 at 22:08