Model State not checked in MVC WebAPI when called from Client - c#

I have the following class defintion in my WebAPI:
public class LogIt
{
[Required(ErrorMessage = "Log ID required")]
public int LogID { get; set; }
public int StatusID { get; set; }
public string Message { get; set; }
public int UserID { get; set; }
}
When calling it with Restlet directly, it validated correctly and err's when LogID is not included in the JSON payload.
Here is a shell of what is called in the controller:
public IHttpActionResult PostTrace([FromBody]LogIt log)
{
if (ModelState.IsValid) {
LogIt.Insert(log);
}
return Ok();
}
From my client application, I have the following class defined:
public class LogIt
{
public int LogID { get; set; }
public int StatusID { get; set; }
public string Message { get; set; }
public int UserID { get; set; }
}
And the code that calls:
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:8080/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
LogIt log = new LogIt();
log.StatusID = 1;
log.Message = "Test";
log.UserID = 1;
HttpResponseMessage response = await client.PostAsJsonAsync("log/trace/insert", log);
response.EnsureSuccessStatusCode();
However, the LogID never gets checked in the WebAPI as a "Required" field, so modelstate is valid, and when the DB insert/update happens, I get an error (obviously)
Any idea why this is? Should I bring the attribute for required field into the client app as well?

When you call LogIt log = new LogIt(); in your client application, LogID will be set to 0 by the constructor. This is a value, so will be sent in your JSON payload as "logID: 0", for example.

Related

Why API doesn't response correctly for my request?

I am testing request for API from Baselinker. I have created simple app in c#, which takes input as JSON file with parameters, then converts it to API request model, send it to API and receive response.
But I have problem with one request, https://api.baselinker.com/index.php?method=getOrders. When I try to get orders from my test account by this request I got response "Order source does not exist.", idk why - I have checked every variable in my class which represents this request but didn't find anything wrong. When I do the same on testing API request site (https://api.baselinker.com/index.php?tester) it works correct.
Here is my source code:
Class representing getOrder request:
public class GetOrders : IRequest<GetOrders.Response> {
[JsonPropertyName("order_id")]
public int? OrderId { get; set; }
[JsonPropertyName("date_confirmed_from")]
public int? DateConfirmedFrom { get; set; }
[JsonPropertyName("date_from")]
public int? DateFrom { get; set; }
[JsonPropertyName("id_from")]
public int? IdFrom { get; set; }
[JsonPropertyName("get_unconfirmed_orders")]
public bool? GetUnconfirmedOrders { get; set; }
[JsonPropertyName("include_custom_extra_fields")]
public bool? IncludeCustomExtraFields { get; set; }
[JsonPropertyName("status_id")]
public int? StatusId { get; set; }
[JsonPropertyName("filter_email")]
public string? FilterEmail { get; set; }
[JsonPropertyName("filter_order_source")]
public string? FilterOrderSource { get; set; }
[JsonPropertyName("filter_order_source_id")]
public int? FilterOrderSourceId { get; set; }
public class Product {
```Product class properties...```
}
public class Order {
```Order class properties...```
}
public class Response : Output {
[JsonPropertyName("orders")]
public List<Order> Orders { get; set; }
}
}
Class sending requests:
public class BaselinkerRequestManager {
private string _token;
private const string _url = "https://api.baselinker.com/connector.php";
public BaselinkerRequestManager(string token) { _token = token; }
private string GetRequestMethodName(object userRequest) {
return JsonNamingPolicy.CamelCase.ConvertName(userRequest.GetType().Name);
}
private RestRequest CreateRequest(string method, object parameters) {
var request = new RestRequest();
request.Method = Method.Post;
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("X-BLToken", _token);
request.AddParameter("method", method);
if ( parameters != null ) {
request.AddParameter("parameters", JsonSerializer.Serialize(parameters));
}
return request;
}
private async Task<RestResponse> ExecuteRequestAsync(RestClient client, RestRequest request) {
return await client.ExecuteAsync(request);
}
//TResponse - generic which represents Response Class in each Request
public async Task<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> userRequest) {
var client = new RestClient(_url);
var method = GetRequestMethodName(userRequest);
var request = CreateRequest(method, userRequest);
var response = await ExecuteRequestAsync(client, request);
return JsonSerializer.Deserialize<TResponse>(response.Content);
}
}
Here is call:
var requestManager = new BaselinkerRequestManager("token_to_connect");
//this request doesn't need parameters so i dont have to initialize it
var r_getOrders = await requestManager.SendRequestAsync(new Requests.Orders.GetOrders());

C# HttpClient Serialize/Deserialize new

I am writing a basis class to simplify REST API calls. I am now at the point to decide how to handle the requests and responses from the perspective of the Serialize/Deserialize objects.
Below is a POST request example. Basically it receives the path and handle the response using as JToken.Parse.
Example
public async Task<Uri> PostCreateAsync(string entitySetName, object body)
{
try
{
using (var message = new HttpRequestMessage(HttpMethod.Post, entitySetName))
{
message.Content = new StringContent(SystemTextJson.SerializeToString(body));
message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
using (HttpResponseMessage response = await SendAsync(message))
{
return new Uri(response.Headers.GetValues("OData-EntityId").First());
}
}
}
catch (Exception ex)
{
throw ex;
}
}
Usage
public interface IRecord
{
Guid Id { get; set; }
string Name { get; set; }
}
public class Contact : IRecord
{
public Guid Id { get; set; } = Guid.Empty;
public string FullName { get; set; } = null!;
public string LastName { get; set; } = null!;
public string JobTitle { get; set; } = null!;
}
var contact = new Contact()
{
LastName = "Gil",
JobTitle = "Developer",
FullName = "Andre Gil"
};
var response = await _api.PostCreateAsync("contacts", contact);
First problem here. How to provide the api name "contacts" generic? Json decoration?
Second, the Id attribute cannot be sent through on POST operation. Of course, I could use JsonIgnore. But the "Id" must be sent through in case of PATCH or PUT.

In .net5.0 - Extranal API response time takes too long time - httpclient

I'm trying to get response a external API using httpclint in .netcore5.0.
Initially I got timeout exception. So I add client.Timeout = Timeout.InfiniteTimeSpan; after adding this response is come. but it takes more than 20 mins.
But I browser I can get API result within milliseconds.
How can I get response from API with a short time. Any idea to decrease this responding time?
startup.cs
services.AddHttpClient<IHolidayService, HolidayService>("PublicHolidaysApi", c => c.BaseAddress = new Uri("https://api.xmltime.com"));
service.cs
public class HolidayService : IHolidayService
{
private readonly IHttpClientFactory _clientFactory;
private readonly HttpClient _client;
public HolidayService(HttpClient client)
{
_client = client;
client.Timeout = Timeout.InfiniteTimeSpan;
}
public HolidayService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
_client = clientFactory.CreateClient("PublicHolidaysApi");
}
public async Task<Holiday> GetHolidays(string country,int year)
{
string url = string.Format($"/holidays?accesskey="MyAccessKey"&secretkey="MySecretKey"&version=3&country=ro&year=2021&lang=en");
var result = new Holiday();
using (var cts = new CancellationTokenSource(Timeout.InfiniteTimeSpan))
{
var response = await _client.GetAsync(url, cts.Token).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
result = await JsonSerializer.DeserializeAsync<List<Holiday>>(responseStream);
}
else
{
throw new HttpRequestException(response.ReasonPhrase);
}
}
return result;
}
}
}
model
public class Holiday
{
[JsonPropertyName("urlid")]
public string UrliId { get; set; }
[JsonPropertyName("url")]
public string Url { get; set; }
[JsonPropertyName("country")]
public Country Country { get; set; }
[JsonPropertyName("name")]
public Name Name { get; set; }
[JsonPropertyName("oneliner")]
public OneLiner OneLiner { get; set; }
[JsonPropertyName("date")]
public Date Date { get; set; }
[JsonPropertyName("types")]
public List<string> Types { get; set; }
[JsonPropertyName("uid")]
public string UId { get; set; }
}
public class Country
{
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
public class Name
{
[JsonPropertyName("lang")]
public string Lang { get; set; }
[JsonPropertyName("text")]
public string Text { get; set; }
}
public class OneLiner
{
[JsonPropertyName("lang")]
public string Lang { get; set; }
[JsonPropertyName("text")]
public string Text { get; set; }
}
public class Date
{
[JsonPropertyName("iso")]
public string iso { get; set; }
[JsonPropertyName("datetime")]
public DateTime? Datetime { get; set; }
}
}
There are so many possibilities in this situation and I can only give you a way to solve the problem.
First of all, we need to locate the reason why it is so slow. Is it the server or the client?
We can use packet capture tools such as Fiddler ,and then observe the corresponding network requests.
If client had send but server not response , you should think about the api limit...
And if not, the request are not send at all, may be you should the check the connection pool of the HttpClient, or the WorkThreadPool of dotnet.
there are a few problems with your question.
it doesn't compile.
it is incomplete.
it has sensitive data.
but I can get the data from API in no time. just open this link https://dotnetfiddle.net/ryjakT and run the program.
I changed few things
Return Type, it should be List
var result = new Holiday(); to var result = new List();
I am using Newtonsoft.Json for Deserialization.
you were trying to Deserialize to an incorrect model, it should be Root.

Post with HttpClient doesn't bind model

Simple post from azure function to API
using (var response = await httpClient.PostAsJsonAsync(installationServiceUrl, deviceInstallation.ToRequestBody()))
{...}
API receives the request, but cannot bind model from Request
But Request.Content is not null and contains sent JSON object. Content-Type header set to application/json.
Any suggestions?
Update: As I got it, some how API thinks that Model is simple string value (locationId), at least that is how I understand from from ModelState.Keys collection. It contains only locationId.
Update: ToRequestBody method just changes the shape of the object
public static DeviceInstallationRequest ToRequestBody(this DeviceInstallation deviceInstallation)
{
return new DeviceInstallationRequest()
{
InstallationId = deviceInstallation.InstallationId,
Name = deviceInstallation.Name,
StartDateTime = deviceInstallation.StartDateTime,
EndDateTime = deviceInstallation.EndDateTime,
CreatedDateTime = deviceInstallation.CreatedDateTime,
InstallationType = deviceInstallation.InstallationType,
Production = deviceInstallation.Production,
Default = deviceInstallation.Default
}
}
And expected model on API side:
public class BindDeviceInstallationRequest
{
[Required]
public string InstallationId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public DateTime StartDateTime { get; set; }
[Required]
public DateTime EndDateTime { get; set; }
[Required]
public DateTime CreatedDateTime { get; set; }
[Required]
public InstallationType InstallationType { get; set; }
[Required]
public bool Production { get; set; }
[Required]
public bool Default { get; set; }
}
In case it is an encoding issue try building the content yourself and sending it to the server,
DeviceInstallationRequest model = deviceInstallation.ToRequestBody();
string json = JsonConvert.SerializeObject(model);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using (var response = await httpClient.PostAsync(installationServiceUrl, content)) {
//...
}
that way you have complete control of what is being sent to the server.
While debugging, inspect the raw JSON from the client and what is received on the server.

How to pass parameters to array class in webapi?

I am new to asp.net mvc webapi.I am create one webapi service.In this service I am sending parameter as an array class.
Below is my service :
[AcceptVerbs("GET", "POST")]
public HttpResponseMessage addBusOrder(string UserUniqueID, int PlatFormID,
string DeviceID, int RouteScheduleId,
string JourneyDate, int FromCityid,
int ToCityid, int TyPickUpID,
Contactinfo Contactinfo, passenger[] pass)
{
//done some work here
}
public class Contactinfo
{
public string Name { get; set; }
public string Email { get; set; }
public string Phoneno { get; set; }
public string mobile { get; set; }
}
public class passenger
{
public string passengerName { get; set; }
public string Age { get; set; }
public string Fare { get; set; }
public string Gender { get; set; }
public string Seatno { get; set; }
//public string Seattype { get; set; }
// public bool Isacseat { get; set; }
}
Now how to pass passenger and contactinfo parameters to the above service.
Is there any changes in webapiconfig file?
i want to pass passenger details like this:
passengername="pavan",
age="23",
Gender="M",
passengername="kumar",
Gender="M",
Age="22
It will be much neater if you can create model of your parameter. To pass them from client side, you need to format them using one of data-interchange format. I prefer use JSON provided by Newtonsoft.Json library. Sending process is handled by HttpClient class provided by System.Net.Http namespace. Here is some sample:
Server Side
//Only request with Post Verb that can contain body
[AcceptVerbs("POST")]
public HttpResponseMessage addBusOrder([FromBody]BusOrderModel)
{
//done some work here
}
//You may want to separate model into a class library so that server and client app can share the same model
public class BusOrderModel
{
public string UserUniqueID { get; set; }
public int PlatFormID { get; set; }
public string DeviceID { get; set; }
public int RouteScheduleId { get; set; }
public string JourneyDate { get; set; }
public int FromCityid { get; set; }
public int ToCityid { get; set; }
public int TyPickUpID { get; set; }
public Contactinfo ContactInfo { get; set; }
public passenger[] pass { get; set; }
}
Client Side
var busOrderModel = new BusOrderModel();
var content = new StringContent(JsonConvert.SerializeObject(busOrderModel), Encoding.UTF8, "application/json");
using (var handler = new HttpClientHandler())
{
using (HttpClient client = new HttpClient(handler, true))
{
client.BaseAddress = new Uri("yourdomain");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return await client.PostAsync(new Uri("yourdomain/controller/addBusOrder"), content);
}
}
Here's how you can do it:
First, since you are passing two objects as parameters we'll need a new class to hold them (because we can only bind one parameter to the request's content):
public class PassengersContact
{
public Passenger[] Passengers { get; set; }
public Contactinfo Contactinfo { get; set; }
}
and now for your controller (this is just a test controller):
[RoutePrefix("api")]
public class DefaultController : ApiController
{
[HttpPost]
// I prefer using attribute routing
[Route("addBusOrder")]
// FromUri means that the parameter comes from the uri of the request
// FromBody means that the parameter comes from body of the request
public IHttpActionResult addBusOrder([FromUri]string userUniqueId,
[FromUri]int platFormId,
[FromUri]string deviceId, [FromUri]int routeScheduleId,
[FromUri]string journeyDate, [FromUri]int fromCityid,
[FromUri]int toCityid, [FromUri]int tyPickUpId,
[FromBody]PassengersContact passengersContact)
{
// Just for testing: I'm returning what was passed as a parameter
return Ok(new
{
UserUniqueID = userUniqueId,
PlatFormID = platFormId,
RouteScheduleId = routeScheduleId,
JourneyDate = journeyDate,
FromCityid = fromCityid,
ToCityid = toCityid,
TyPickUpID = tyPickUpId,
PassengersContact = passengersContact
});
}
}
Your request should look something like this:
POST http://<your server's URL>/api/addBusOrder?userUniqueId=a&platFormId=10&deviceId=b&routeScheduleId=11&journeyDate=c&fromCityid=12&toCityid=13&tyPickUpId=14
Content-Type: application/json
Content-Length: 110
{
"passengers" : [{
"passengerName" : "name",
"age" : 52
/* other fields go here */
}
],
"contactinfo" : {
"name" : "contact info name",
/* other fields go here */
}
}
Notice the api/addBusOrder comes from concatenating the values of the RoutePrefix/Route attributes.

Categories