REST API Connection Editor - POST Example, please?

Can you give me a small example of adding a post function to a Rest Connection in Radzen Blazor Studio? I cannot get it to work in the Rest API Data Connection Editor. All my GETs work, but my POSTs refuse to work at all. I have been working on this for at least a week and have tried several different things, but I cannot get it to work.
Thank You

What seems to be the problem? Creating a post method shouldn't be much different than GET requests. Is there any runtime exception or maybe inferring the result doesn't work?

I have a Web API running on Azure, Protected by Entra ID.I created custom request and response objects because I would have expected a proper response using the following Request / Response.
Request:
{
"originZIPCode": "94538",
"destinationZIPCode": "94560",
"weight": 0.5,
"length": 6,
"width": 5,
"height": 0.5,
"mailClass": "USPS_GROUND_ADVANTAGE",
"processingCategory": "MACHINABLE",
"destinationEntryFacilityType": "NONE",
"rateIndicator": "DR",
"priceType": "COMMERCIAL",
"mailingDate": "2024-06-22",
"accountType": "EPS",
"accountNumber": "1000071517"
}

Response: {
"totalBasePrice": 6.83,
"rates": [
{
"SKU": "DUXR0XXXXC01010",
"description": "USPS Ground Advantage Machinable Dimensional Rectangular",
"priceType": "COMMERCIAL",
"price": 6.83,
"weight": 0.5,
"dimWeight": 0,
"fees": ,
"startDate": "2024-01-21",
"endDate": "",
"warnings": [
{
"warningCode": "001",
"warningDescription": "NSA rate not found for request. Published rate returned."
}
],
"mailClass": "USPS_GROUND_ADVANTAGE",
"zone": "01"
}
]
}

It works as expected when I run it using the Swagger interface on my Web API Server. But when I execute it through the Blazor Studio REST Data Connection Editor, either with or without custom request and response objects, it returns a failure with a message of 'Unexpected token 'F', ' Failed to fetch' is not valid JSON. Do you have any idea what I am missing?

How is the method configured in the UI? What parameter do you pass when the request is made during infer?

I pass only the JSON input, after which the API processes the JSON appropriately

Can you paste a screenshot of the method configuration in the UI? I also need to see what parameters you are providing when you click "infer".

This is using the Dynamic Type

I also have a class designed, describing the Web Order type:

// ***********************************************************************
// Assembly : VisionSuiteShipManager.Server
// Author : William Eisenman
// Created : 04-13-2024
//
// Last Modified By : William Eisenman
// Last Modified On : 04-30-2024
// ***********************************************************************
//
// Copyright � 2021-2024 by FlexOps, LLC. All rights reserved.
//
//


// ***********************************************************************
#nullable enable
using System.Globalization;
using System.Text.Json.Serialization;

namespace VisionSuiteShipManager.Server.Models.VisionSuiteCoreApi.Orders;

///


/// Class WebOrder.
///

[Serializable]
public class WebOrder
{
///
/// Initializes a new instance of the class.
///

/// The date and time is outside the range of dates supported by the calendar used by provider.
public WebOrder()
{
OrderInfo = new OrderInfo
{
CustomerId = -1,
OrderNumber = null,
OrderDate = DateTimeOffset.Now.LocalDateTime.ToString(CultureInfo.InvariantCulture),
RushOrder = false,
ShipMethod = "Priority Mail Return Service",
FreightTerm = "Prepaid",
PartiesRelated = false,
DutyPaidBy = "Recipient",
SignatureDelivery = 0,
ReturnLabelService = 0,
SpecialHandling = 0,
AppointmentRequired = false,
AsnRequired = false,
SsccRequired = false,
ReturnLabelRequired = false,
PartialShippingAllowed = false
};
AddressInfo = ;
CustomFields = ;
OrderItems = ;
OrderAmounts = ;
}

/// <summary>
/// Initializes a new instance of the <see cref="WebOrder"/> class.
/// </summary>
/// <param name="orderInfo">The order information.</param>
/// <param name="addressInfo">The address information.</param>
/// <param name="customFields">The custom fields.</param>
/// <param name="orderItems">The order items.</param>
/// <param name="orderAmounts">The order amounts.</param>
public WebOrder(OrderInfo orderInfo, List<OrderAddress>? addressInfo, List<OrderCustomField>? customFields,
    List<OrderItem>? orderItems, List<OrderAmount>? orderAmounts)
{
    OrderInfo = orderInfo;
    AddressInfo = addressInfo;
    CustomFields = customFields;
    OrderItems = orderItems;
    OrderAmounts = orderAmounts;
}

/// <summary>
///     Gets or sets the order information.
/// </summary>
/// <value>The order information.</value>
[JsonPropertyName("OrderInfo")]
public required OrderInfo OrderInfo { get; set; }

/// <summary>
///     Gets or sets the ship address, and so on.
/// </summary>
/// <value>The ship, etc. address information.</value>
[JsonPropertyName("AddressInfo")]
[field: NonSerialized]
public required List<OrderAddress>? AddressInfo { get; set; }

/// <summary>
///     Gets or sets the custom fields.
/// </summary>
/// <value>The custom fields.</value>
[JsonPropertyName("CustomFields")]
[field: NonSerialized]
public List<OrderCustomField>? CustomFields { get; set; }

/// <summary>
///     Gets or sets the order items.
/// </summary>
/// <value>The order items.</value>
[JsonPropertyName("OrderItems")]
[field: NonSerialized]
public required List<OrderItem>? OrderItems { get; set; }

/// <summary>
///     Gets or sets the order amounts.
/// </summary>
/// <value>The order amounts.</value>
[JsonPropertyName("OrderAmounts")]
[field: NonSerialized]
public required List<OrderAmount>? OrderAmounts { get; set; }

}

Should I have it and the associated subtypes defined in the default directory, or can I place it in an organization subdirectory?

Here is a smaller post JSON:

And Here is the actual JSON:
{
"originZIPCode": "94538",
"destinationZIPCode": "94560",
"weight": 7,
"length": 9,
"width": 0.25,
"height": 6,
"mailClass": "USPS_GROUND_ADVANTAGE",
"processingCategory": "MACHINABLE",
"destinationEntryFacilityType": "NONE",
"rateIndicator": "DR",
"priceType": "COMMERCIAL",
"accountType": "EPS",
"accountNumber": "1000071517",
"mailingDate": "2024-07-01"
}

I forgot to add the fact that I am expecting the following JSON Response:

{
"totalBasePrice": 8.43,
"rates": [
{
"SKU": "DUXR0XXXXC01070",
"description": "USPS Ground Advantage Machinable Dimensional Rectangular",
"priceType": "COMMERCIAL",
"price": 8.43,
"weight": 7,
"dimWeight": 0,
"fees": ,
"startDate": "2024-01-21",
"endDate": "2024-07-13",
"warnings": [
{
"warningCode": "001",
"warningDescription": "Contract rate not found for request. Published rate returned."
}
],
"mailClass": "USPS_GROUND_ADVANTAGE",
"zone": "01"
}
]
}

Hi @FastTrak,

What are you posting when you click the infer button? I mean what is the value you provide for the parameter? Also is the server running at the time you make the infer? Getting status 0 and time 0ms means that the request has failed immediately without additional details.

Let's use a small post here: Let's just use a small post here:

  • My first question is why it's pointed to a local server when it's configured in my appconfig.json as being pointed to Azure and should be correctly listed as:
    https://visionsuitecoredevapi.azurewebsites.net/v3/RateCalculator/postUspsSearchDomesticBaseRates?
  • I went and started my local server and submitted the following JSON parameter:
    {
    "originZIPCode": "94538",
    "destinationZIPCode": "94560",
    "weight": 7,
    "length": 9,
    "width": 0.25,
    "height": 6,
    "mailClass": "USPS_GROUND_ADVANTAGE",
    "processingCategory": "MACHINABLE",
    "destinationEntryFacilityType": "NONE",
    "rateIndicator": "DR",
    "priceType": "COMMERCIAL",
    "accountType": "EPS",
    "accountNumber": "1000071517",
    "mailingDate": "2024-07-01"
    }
    Response Expected:

{
"totalBasePrice": 8.43,
"rates": [
{
"SKU": "DUXR0XXXXC01070",
"description": "USPS Ground Advantage Machinable Dimensional Rectangular",
"priceType": "COMMERCIAL",
"price": 8.43,
"weight": 7,
"dimWeight": 0,
"fees": ,
"startDate": "2024-01-21",
"endDate": "2024-07-13",
"warnings": [
{
"warningCode": "001",
"warningDescription": "Contract rate not found for request. Published rate returned."
}
],
"mailClass": "USPS_GROUND_ADVANTAGE",
"zone": "01"
}
]
}

Response Received:

"type": "RFC 9110 - HTTP Semantics",
"title": "Unsupported Media Type",
"status": 415,
"traceId": "00-6e5cfc56ce8172e278c58717b95c3427-4e0bc8cd7266a15b-00"
}

What is this? Do I have to send a Content-Type Header? I thought that it was automatically generated.

I can't tell that from the screenshots provided so far. It depends on the URL entered in the first step.

No HTTP headers are added automatically during infer. You can try adding Content-Type "application/json" as a header to see if it makes a difference.

I used your inferred code exactly, and when I compiled it in Visual Studio 2022 v 17.10.3, it failed for misuse of the Header "Content-Type." Why is that? Do you use a different version of the dotNET Framework?

Here is the exact content of the error:
Misused header name, 'Content-Type'. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.

The following is a copy of my C# code:

public async Task<PostUspsSearchDomesticBaseRatesResponse?> PostUspsSearchDomesticBaseRates(
PostUspsSearchDomesticBaseRatesRequest requestBody)
{
    if (httpClient.BaseAddress == null) return null;
    var uri = new Uri(httpClient.BaseAddress, $"api/v3/RateCalculator/postUspsSearchDomesticBaseRates");

    // Set up the Request Headers ...
    httpClient.DefaultRequestHeaders.Accept.Clear();
    httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
    httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "*/*");
    httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate, br");
    httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Connection", "keep-alive");

    var request = new HttpRequestMessage(HttpMethod.Post, uri);
    request.Content = JsonContent.Create<PostUspsSearchDomesticBaseRatesRequest>(requestBody);

    OnPostUspsSearchDomesticBaseRates(request);

    await AuthorizeRequest(request).ConfigureAwait(false);

    var response = await httpClient.SendAsync(request).ConfigureAwait(false);
    
    if (!response.IsSuccessStatusCode)
    {
        return null;
    }

    OnPostUspsSearchDomesticBaseRatesResponse(response);

    var result = await response.Content
        .ReadFromJsonAsync<PostUspsSearchDomesticBaseRatesResponse>()
        .ConfigureAwait(false);
    return result;
}

This code isn't generated by Radzen Blazor Studio. We don't know why it may fail.

Ok, sorry. Here is the inferred code directly from Radzen Blazor Studio:

partial void OnPostUspsSearchDomesticBaseRates(HttpRequestMessage request);
partial void OnPostUspsSearchDomesticBaseRatesResponse(HttpResponseMessage response);

    public async Task<VisionSuiteShipManager.Server.Models.VisionSuiteCoreApi.PostUspsSearchDomesticBaseRate> PostUspsSearchDomesticBaseRates(string contentType, dynamic requestBody)
    {
        var uri = new Uri(httpClient.BaseAddress, $"api/v3/RateCalculator/postUspsSearchDomesticBaseRates");

        var request = new HttpRequestMessage(HttpMethod.Post, uri);
        request.Headers.Add("Content-Type", $"{contentType}");
        request.Content = JsonContent.Create<dynamic>(requestBody);

        OnPostUspsSearchDomesticBaseRates(request);

        await AuthorizeRequest(request);

        var response = await httpClient.SendAsync(request);

        response.EnsureSuccessStatusCode();

        OnPostUspsSearchDomesticBaseRatesResponse(response);

        return await response.Content.ReadFromJsonAsync<VisionSuiteShipManager.Server.Models.VisionSuiteCoreApi.PostUspsSearchDomesticBaseRate>();
    }

Hi @FastTrak,

I see now. The problem is described here. The currently generated code would indeed fail for the content-type header (although request.Content = JsonContent.Create<dynamic>(requestBody); would still correctly set it). We will make two changes with the next release:

  1. Implicitly set content-type to application/json during infer when there is a body parameter
  2. Use request.Headers.TryAddWithoutValidation("Content-Type", $"{contentType}"); to avoid validation exceptions.

As a temporary workaround you can try changing the code to this:

public async Task<VisionSuiteShipManager.Server.Models.VisionSuiteCoreApi.PostUspsSearchDomesticBaseRate> PostUspsSearchDomesticBaseRates(string contentType, dynamic requestBody)
    {
        var uri = new Uri(httpClient.BaseAddress, $"api/v3/RateCalculator/postUspsSearchDomesticBaseRates");

        var request = new HttpRequestMessage(HttpMethod.Post, uri);
        request.Headers.TryAddWithoutValidation("Content-Type", $"{contentType}");
        request.Content = JsonContent.Create<dynamic>(requestBody);

        OnPostUspsSearchDomesticBaseRates(request);

        await AuthorizeRequest(request);

        var response = await httpClient.SendAsync(request);

        response.EnsureSuccessStatusCode();

        OnPostUspsSearchDomesticBaseRatesResponse(response);

        return await response.Content.ReadFromJsonAsync<VisionSuiteShipManager.Server.Models.VisionSuiteCoreApi.PostUspsSearchDomesticBaseRate>();
    }

We should release those changes by the end of the week.