Query Elasticsearch from C# using webclient throws 400 bad request? - c#

I am trying to call elasticsearch query using webclient in C#. My API url is correctly working with angular ajax call (POST) with passing some parameters and without.
The JSON I am sending in my query is:
{
'from': 0,
'size': 20,
'sort': ['_score',
{
'publishDate': {
'order': 'desc'
}
}],
'query': {
'bool': {
'should': [{
'query_string': {
'query': 'Test*'
}
},
{
'match_phrase': {
'reportName': 'Test'
}
},
{
'match': {
'researcher': 'Test'
}
}]
}
},
'filter': {
'bool': {
'must': [{
'numeric_range': {
'price': {
'lte': 99999
}
}
},
{
'numeric_range': {
'publishDate': {
'gte': '1957-06-25T07: 30: 27.806Z'
}
}
},
{
'numeric_range': {
'qualityScore': {
'gt': 0
}
}
},
{
'terms': {
'reportLanguageMaps': ['English']
}
}],
'should': []
}
},
'min_score': 0.005
}
This is my code:
var queryString = "{'from':0,'size':20,'sort':['_score',{'publishDate':{'order':'desc'}}],'query':{'bool':{'should':[{'query_string':{'query':'Test * '}},{'match_phrase':{'reportName':'Test'}},{'match':{'researcher':'Test'}}]}},'filter':{'bool':{'must':[{'numeric_range':{'price':{'lte':99999}}},{'numeric_range':{'publishDate':{'gte':'1957-06-25T07: 30:27.806Z'}}},{'numeric_range':{'qualityScore':{'gt':0}}},{'terms':{'reportLanguageMaps':['English']}}],'should':[]}},'min_score':0.005}";
WebClient testCLient = new WebClient();
var result = testCLient.UploadString(ESConstant.ESAPIConnection, "POST", queryString);
Now above code is giving me 400 bad request. If would not pass queryString it will success call and give me result (json , but without apply filter is no meaning for me).

Related

Is there a way for get NSwag Swagger to generate a client that can consume IAsyncEnumerable endpoint?

I have an endpoint that returns an IAsyncEnumerable
[HttpPost("GetByDates")]
[ProducesResponseType(typeof(IAsyncEnumerable<DayModel>), StatusCodes.Status200OK)]
public async IAsyncEnumerable<DayModel> GetByDates([FromBody] DayModelGetByDatesRequest request)
{
await foreach (var dayModel in _dayService.GetAsync(request.channelGuid, request.dates.ToArray(), request.onlyPublished, request.IncludeDiscardedScheduledItems))
{
yield return dayModel;
};
}
The generated .json schema looks like this:
"/Private/Days/GetByDates": {
"post": {
"tags": [
"Days"
],
"operationId": "Days_GetByDates",
"requestBody": {
"x-name": "request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DayModelGetByDatesRequest"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Day"
}
}
}
}
}
}
}
}
and the Nswag is configured like this:
services.AddOpenApiDocument(configure =>
{
configure.Title = "MyAppName (Private)";
configure.DocumentName = "private";
configure.SchemaType = SchemaType.OpenApi3;
configure.SchemaNameGenerator = new CustomNameGenerator();
configure.AddOperationFilter(new RequireUserHeaderParameterFilter().Process);
configure.AddSecurity("Bearer", new OpenApiSecurityScheme()
{
In = OpenApiSecurityApiKeyLocation.Header,
Description = "Please enter the word \"Bearer\" followed by space and token",
Name = "Authorization",
Type = OpenApiSecuritySchemeType.ApiKey,
});
configure.ApiGroupNames = new string[] { "Private" };
});
And another project uses the .json schema to genereate a client of its own that seems to use Newtonsoft Json instead of System.Text.Json
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.18.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class TablaApiClient
{
private string _baseUrl = "https://localhost:5102";
private System.Net.Http.HttpClient _httpClient;
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
public TablaApiClient(System.Net.Http.HttpClient httpClient)
{
_httpClient = httpClient;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
{
var settings = new Newtonsoft.Json.JsonSerializerSettings();
UpdateJsonSerializerSettings(settings);
return settings;
}
public string BaseUrl
{
get { return _baseUrl; }
set { _baseUrl = value; }
}
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
The endpoint doesn't serialize the IAsyncEnumerable and return an ICollection instead:
The Swagger is configured like so:
services.AddOpenApiDocument(configure =>
{
configure.Title = "My App";
configure.SchemaType = NJsonSchema.SchemaType.OpenApi3;
configure.AddSecurity("AzureAsIdentityProvider", new OpenApiSecurityScheme
{
Type = OpenApiSecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = $"{settings.Instance}/{settings.TenantId}/oauth2/v2.0/authorize",
TokenUrl = $"{settings.Instance}/{settings.TenantId}/oauth2/v2.0/token",
}
}
});
configure.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("AzureAsIdentityProvider"));
});
Is there a way for the generated client to properly serialize and understand it is working towards an IAsyncEnumerable endpoint so that I can work with the stream instead of fully buffered collection?
I read that System.Text.Json serializes IAsyncEnumerable out of the box. Is there a way to get Swagger to use that instead of Newtonsoft?

Message = "Unexpected character encountered while parsing value: [. Path '', line 1, position 1."

I want to show list of departments in my ASP.NET Core MVC view.
Notice that I have 3-tier layers (Data Access + API (to get data from database) + MVC (UI)).
Here is my Json Data that I got from database using a call to the API:
[
{
"id": 3,
"name": "Sales"
},
{
"id": 4,
"name": "PMO"
},
{
"id": 5,
"name": "Research And Development"
},
{
"id": 6,
"name": "Product Management"
},
{
"id": 7,
"name": "HR"
},
{
"id": 8,
"name": "Ava"
},
{
"id": 9,
"name": "IT"
}
]
Here is my C# code using HttpClient to get data from the API:
public async Task<T> GetRequest<T>(string uri)
{
try
{
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = await client.GetAsync(uri))
{
if (response.StatusCode.ToString() == "OK")
{
_logger.LogInformation("Get Request Successed");
//response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
}
return JsonConvert.DeserializeObject<T>(responseBody);
}
}
catch (Exception ex)
{
_logger.LogError("Failed");
return JsonConvert.DeserializeObject<T>(responseBody);
}
}
When I am trying to parse the data from json it returns an Error.
Unexpected character encountered while parsing value: [. Path '', line 1, position 1.
You can parse your string to a strongly typed Model and then de-serialize to it OR you can use dynamic and access the properties as shown below:
You can find a working example here
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var myJsonString=#"[{'id':3,'name':'Sales'},{'id':4,'name':'PMO'},{'id':5,'name':'Research And Development'},{'id':6,'name':'Product Management'},{'id':7,'name':'HR'},{'id':8,'name':'Ava'},{'id':9,'name':'IT'}]";
var result =JsonConvert.DeserializeObject<List<Root>>(myJsonString);
Console.WriteLine("Example using Model: \n");
foreach(var item in result)
{
Console.WriteLine(item.id);
Console.WriteLine(item.name);
}
Console.WriteLine("\n");
Console.WriteLine("Example using Dynamic: \n");
//Example using dynamic
var resultDynamic=JsonConvert.DeserializeObject<dynamic>(myJsonString);
foreach(var item in resultDynamic)
{
Console.WriteLine(item["id"]);
Console.WriteLine(item["name"]);
}
}
}
public class Root
{
public int id { get; set; }
public string name { get; set; }
}
Output:
Example using Model:
3
Sales
4
PMO
5
Research And Development
6
Product Management
7
HR
8
Ava
9
IT
Example using Dynamic:
3
Sales
4
PMO
5
Research And Development
6
Product Management
7
HR
8
Ava
9
IT

HTTP client method null exception

I have an API project and I need to develop a web project using the API I wrote some code but not able to find the exception and problem and not getting data from the link.
Here is my Service Code:
public async Task<IEnumerable<AgentReadDto>> GetAgent()
{
IEnumerable<AgentReadDto> agents = new List<AgentReadDto>();
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44331/api/");
var response = client.GetAsync("Agent/GetAllAgent");
response.Wait();
var result = response.Result;
if (result.IsSuccessStatusCode)
{
var readTask =JsonConvert.DeserializeObject<IList<AgentReadDto>>(await result.Content.ReadAsStringAsync());
agents = readTask;
}
}
return agents;
}
And my controller code is look like this:
public IActionResult AgentLists()
{
var agentsList = _agentRespositoryWeb.GetAgent();
if (agentsList != null )
{
ViewBag.Message = "There was a problem retrieving agent from the database or no agents exists";
}
ViewBag.SuccessMessage = TempData["SuccessMessage"];
return View(agentsList);
}
My api return the value following:
{
"agentDetail": [
{
"usersId": 85,
"firstName": "Amit",
"lastName": "One",
"gender": "Male",
"informationTips": [
{
"video": "https://www.w3schools.com/html/movie.mp4"
},
{
"video": "https://www.w3schools.com/html/movie.mp4"
},
]
},
{
"usersId": 86,
"firstName": "Amit",
"lastName": "Two",
"gender": "Male",
"informationTips": [
{
"video": "https://www.w3schools.com/html/movie.mp4"
}
]
}
]
}
For exception I added image there is three image that take screen on the different steps:
Your model is set to IEnumerable<AgentReadDto>, but you've forgotten to await the call to GetAgent inside of the AgentLists action. This means there's a mismatch between what the view expects (IEnumerable<AgentReadDto>) and what it receives (Task<IEnumerable<AgentReadDto>>).
To fix this, convert AgentLists to an async method and then await the call to GetAgent. Here's a fixed version of the AgentLists action:
public async Task<IActionResult> AgentLists()
{
var agentsList = await _agentRespositoryWeb.GetAgent();
if (agentsList != null)
{
ViewBag.Message =
"There was a problem retrieving agent from the database or no agents exists";
}
ViewBag.SuccessMessage = TempData["SuccessMessage"];
return View(agentsList);
}
It looks like you also have a mismatch between the type you expect to be returned and the JSON actually being returned. The JSON represents an object with a list inside of it, but you're attempting to parse it as a simple list. To fix that, create a wrapper class that matches the structure of the response. For example, create the following class:
public class ApiResponse
{
public IEnumerable<AgentReadDto> AgentDetail { get; set; }
}
Update the deserialization logic to use this new type:
var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(...);
var agentsLit = apiResponse.AgentDetail;

Swift 4 Alamofire upload file with parameters to ASP.NET MVC Controller

I have an ASP.NET MVC Controller defined like so:
[HttpPost]
public string uploadQAImage(FileUploadClass fileUploadClass, HttpPostedFile image)
{
}
And this what the class FileUploadClass looks like.
public class FileUploadClass
{
public string job { get; set; }
public string createdBy { get; set; }
public string itemId { get; set; }
}
What I am trying to do with Alamofire in iOS is call this Controller, I have tried using parameters:
func saveQAPhotos(_ cellHolder: PhotoClass, completion: #escaping (_ result: Dictionary<String, Any>) -> Void)
{
let parameters: Parameters = [
"job" : cellHolder.job!,
"itemId" : cellHolder.itemId!,
"createdBy" : appDelegate.username!
]
let urlComponents = NSURLComponents(string: webservice + "uploadQAImage");
urlComponents?.user = appDelegate.username;
urlComponents?.password = appDelegate.password;
let url = urlComponents?.url;
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
if let data = cellHolder.photo {
multipartFormData.append(data, withName: "image", fileName: "image.png", mimeType: "image/png")
}
}, usingThreshold: UInt64.init(), to: url!, method: .post, headers: nil) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
if let result = response.result.value {
let jsonData = result as! Dictionary<String, Any>
completion(jsonData)
}
}
case .failure(_):
print("error")
}
}
}
But that didn't work on the ASP.NET side I get this error:
can't bind multiple parameters to the request's content
I have also tried sending the data as [AnyHashable: Any] like so:
func saveQAPhotos(_ cellHolder: PhotoClass, completion: #escaping (_ result: Dictionary<String, Any>) -> Void)
{
var jsonDict = [AnyHashable: Any]()
jsonDict["job"] = cellHolder.job
jsonDict["itemId"] = cellHolder.itemId
jsonDict["createdBy"] = appDelegate.username
let jsonData: Data? = try? JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted)
let urlComponents = NSURLComponents(string: webservice + "uploadQAImage");
urlComponents?.user = appDelegate.username;
urlComponents?.password = appDelegate.password;
let url = urlComponents?.url;
Alamofire.upload(multipartFormData: { (multipartFormData) in
multipartFormData.append(jsonData!, withName: "fileUploadClass", mimeType: "application/json")
if let data = cellHolder.photo {
multipartFormData.append(data, withName: "image", fileName: "image.png", mimeType: "image/png")
}
}, usingThreshold: UInt64.init(), to: url!, method: .post, headers: nil) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
if let result = response.result.value {
let jsonData = result as! Dictionary<String, Any>
completion(jsonData)
}
}
case .failure(_):
print("error")
}
}
}
Same error as before
can't bind multiple parameters to the request's content
Now when I change the ASP.NET Controller to only get FileUploadClass like so:
[HttpPost]
public string uploadQAImage(FileUploadClass fileUploadClass)
{
}
I get this error:
Object reference not set to an instance of an object.
I can only assume if I do fix the can't bind multiple parameters to the request's content error I will get this error next.
I am pretty sure I am sending the data incorrectly, so I guess my question is how do I send data from Alamofire upload method to an ASP.NET MVC method?

.Net Filter For Wrapping JsonResult Actions Response

I've built a Web API application and found an issue (which currently treated badly in my code), the issue summarized in wrapping all Json objects which returned from All API actions with custom nodes(roots).
i.e: I have this json (array) response:
[
{
"Category": "Pages",
"Users": [
{
"ID": "1",
"Fname": "Foo",
"Lname": "Bar"
}
]
}
]
And Need this response:
{
"Object": {
"Body": [
{
"Category": "Pages",
"Users": [
{
"ID": "1",
"Fname": "Foo",
"Lname": "Bar"
}
]
}
]
}
}
So here I just wrapped the response inside {"Object":{"Body": <Response Here>}}
And this I need it to be applied on all API Json responses of type Array.
And for simple Json object response, I need it just to be wrapped like {"Object": <Response Here>}
I wrapped the Json response currently in each controller action by this code:
public JsonResult Categories()
{
return Json(new { Object= new { Body= GetCategoriesList() } }, JsonRequestBehavior.AllowGet);
}
Sure this achievement is so bad because I have to repeat this wrapping in each action.
My Question Is:
How to create ActionFilterAttribute to be called after each action execution to wrap the response as per the above Json sample?
i.e. for creating the filter:
public class JsonWrapper: System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
i.e. for calling the filter:
[JsonWrapper]
public class APIController : Controller
And also to set the response content type in the same filter "application/json"
If suppose here if what you looking for:
public class JsonWrapperAttribute : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
//Check it's JsonResult that we're dealing with
JsonResult jsonRes = context.Result as JsonResult;
if (jsonRes == null)
return;
jsonRes.Data = new { Object = new { Body = jsonRes.Data } }
}
}
Here is how you can use it:
[JsonWrapper]
public JsonResult Index()
{
var data = new
{
a = 1,
b = 2
};
return Json(data, JsonRequestBehavior.AllowGet);
}
Result will be:
{"Object":{"Body":{"a":1,"b":2}}}
To prevent yourself having to repeat wrapping in each action you could either write an extension method which would do the wrapping for you
public static class ControllerExtensions
{
public static JsonResult WrappedJson(this Controller controller, object data, JsonRequestBehavior behavior)
{
return new JsonResult
{
Data = new { Object = new { Body = data } },
JsonRequestBehavior = behavior
};
}
}
or create a new ActionResult class (and add extension methods to return that)
public class WrappedJsonResult : JsonResult
{
public new object Data
{
get
{
if (base.Data == null)
{
return null;
}
return (object) ((dynamic) base.Data).Object.Body;
}
set { base.Data = new {Object = new {Body = value}}; }
}
}

Categories