HTTP client method null exception - c#

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;

Related

Add swagger parameters for unbound parameters in ASP.NET Core

I have an ASP.NET Core 2.2 WebApi and want to upload large files with some additional metadata. The request is a multipart/form-data. Because the files to upload can get quite large, I do not want to read it into memory for processing but rather stream it directly to it's desired destination.
I followed the documentation to disable form value model binding and I also adjusted the maximum request size for the endpoint.
I have tested the endpoint with postman and it works as expected:
However, Swagger obviously does not recognize that there should be parameters for the request. How can I add these parameters to the swagger documentation without defining the parameters in the method's signature?
My endpoint looks like the following example:
[HttpPost]
[DisableFormValueModelBinding]
[DisableRequestSizeLimit]
public async Task<IActionResult> Upload() // "department" and "file" needed in the multipart/form-data
{
// var path = await uploader.UploadAsync(Request);
// return Ok(path);
}
Usually, I would bind the parameters like the following example:
public async Task<IActionResult> Upload([FromForm] string department, [FromForm] IFormFile file)
This works as expected in Swagger but as mentioned above, I do not want to bind the parameters.
For Swashbuckle.AspNetCore version 5 and above some things have changed.
To provide the parameters like Alexander did in his answer, the code would look something like the following:
operation.Parameters.Add(new OpenApiParameter()
{
Name = "department",
Schema = new OpenApiSchema { Type = "string", Format = "string" },
Required = true,
});
operation.Parameters.Add(new OpenApiParameter()
{
Name = "file",
Schema = new OpenApiSchema { Type = "string", Format = "binary" },
Required = true,
});
For some reason however (which I did not investigate further), I was not able to perform an call in the Swagger UI with this approach.
In the end, the following example provided me the result I was looking for:
public class AddUnboundParametersOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var descriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
if (descriptor != null && descriptor.ControllerTypeInfo == typeof(RemoteUpdateController) && descriptor.ActionName == nameof(RemoteUpdateController.Upload))
{
var openApiMediaType = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Required = new HashSet<string> { "department", "file" }, // make the parameter(s) required if needed
Properties = new Dictionary<string, OpenApiSchema>
{
{ "department" , new OpenApiSchema() { Type = "string", Format = "string" } },
{ "file" , new OpenApiSchema() { Type = "string", Format = "binary" } },
}
}
};
operation.RequestBody = new OpenApiRequestBody
{
Content = new Dictionary<string, OpenApiMediaType>
{
{ "multipart/form-data", openApiMediaType }
}
};
}
}
}
You can use IOperationFilter for this. Add the following class, adjust controller and action names
public class AddUnboundParametersOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
var descriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
if (descriptor != null && descriptor.ControllerTypeInfo == typeof(TestController) && descriptor.ActionName == nameof(TestController.Upload))
{
operation.Parameters.Add(new NonBodyParameter()
{
Name = "department",
Type = "string",
Required = true,
In = "formData",
});
operation.Parameters.Add(new NonBodyParameter()
{
Type = "file",
In = "formData",
Name = "file",
Required = true
});
}
}
}
In Startup.cs
services.AddSwaggerGen(c =>
{
c.OperationFilter<AddUnboundParametersOperationFilter>();
//...
});

Patch API call in C# doesn't work, the same call works in swagger

I want to use an external API which has Swagger. In Swagger I am calling this url:
PATCH /rest/inventory/item/{id}
with parameters: X-Auth-Token, id and patchOperations which looks like this:
[
{
"op": "replace",
"path": "price",
"value": 6.2
}
]
And when I call this method with those parameters, it works. I get success code 200 and afterwards when I call the GET method I see that the price of the item has been updated to 6.2.
Now I want to do this in C#. I am already calling some GET methods from the same API successfully. This is my code for the PATCH method:
var model = new Dictionary<string, object>
{
{"op", "replace"},
{"path", "price"},
{"value", 6}
};
var blabla = await _httpProvider.PatchAsync($"https://{url}/server/rest/inventory/item/{id}", model, null, null, null, connection.Request.HeaderParameters);
public async Task<HttpResponseModel> PatchAsync<T>(string uri, T data, HttpClientHandler httpClientHandler = null, TimeSpan? timeout = null, string contentTypes = null, Dictionary<string, string> headerParameters = null)
{
using (var client = CreateHttpClient(httpClientHandler, timeout, contentTypes, headerParameters))
{
var requestContent = new StringContent(JsonConvert.SerializeObject(data));
var response = await client.PatchAsync(new Uri(uri), requestContent);
var result = new HttpResponseModel
{
Success = response.IsSuccessStatusCode,
ResponseContent = await response.Content.ReadAsStringAsync(),
ResponseTime = sw.Elapsed
};
return result;
}
}
Where is my mistake? I am getting error StatusCode: 500, ReasonPhrase: 'Server Error', Version: 1.1, Content: System.Net.Http.StreamContent
The mistake is that you're not pasting the same content, not quite anyway.
Your PATCH example is an array of a objects that has 3 properties, in your example there is only 1 element in the array, but it is still an array. Your C# is serialized into single object.
It's subtle but your JSON that you are sending is actually:
{
"op": "replace",
"path": "price",
"value": 6
}
So instead you need to send your dictionary or other object inside an array:
var model = new List<object> {
{
new Dictionary<string, object>
{
{ "op", "replace"},
{"path", "price"},
{"value", 6}
}
};
Ideally, in c# you would create a class to represent this DTO (Data Transfer Object), it can work with anonymous types or with dictionaries (a Dictionary<string,object> serializes into a single JSON object) but the code gets harder to manage over time.
public class DTO
{
public string op { get; set; }
public string path { get; set; }
public object value { get; set; }
}
...
var model = new List<DTO>
{
new DTO {
op = "replace",
path = "price",
value = 6
}
};

How to query into LUIS programmatically

By default this is how can we send text to LUIS for processing and returns intents.
[Serializable]
public class LuisDialogController : LuisDialog<FAQConversation>
{
private readonly BuildFormDelegate<FAQConversation> _newConversation;
public LuisDialogController(BuildFormDelegate<FAQConversation> newConversation) : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
this._newConversation = newConversation;
}
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
await this.ShowLuisResult(context, result);
}
}
I am wondering how can I send text to LUIS programmatically.
//pseudocode
var foo = new Luis();
var luisIntent = foo.processLanguage("How are you?");
switch(luisIntent)
{
case LuisIntent.Inquiry:
{
//do something; break;
}
default:
{
//do something else; break;
}
}
I've been looking in this solution, however he did not answer by giving a regex.
Would the idea be possible?
In publish section of your LUIS model you have "Resources and Keys" subsection
Below "Endpoint" column you have url(s) that may be used to retrieve data from LUIS by http GET:
https://*.api.cognitive.microsoft.com/luis/v2.0/apps/
*?subscription-key=*&verbose=true&timezoneOffset=0&q=this%20is%20test%20sentence
It will provide you JSON result with structure similar to this:
{
"query": "this is test sentence",
"topScoringIntent": {
"intent": "None",
"score": 0.522913933
},
"intents": [
...
],
"entities": []
}
See more detail and sample C# code here.
Alternatively you may use:
using Microsoft.Bot.Builder.Luis;
...
var model = new LuisModel() {};
var luisService = new LuisService(model);
var result = await luisService.QueryAsync(textToAnalyze, CancellationToken.None);

.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}}; }
}
}

WEB API 1 - Pass JSON ARRAY from .NET CLIENT

I am trying to create one WEB API controller (Service method) which accept an array of a class object.
And then also a .NET client which makes a call to this API method and pass JSON string (array of class object). Issue is I am not able to receive json array contents on server side. Seems some serialization/de-serialization error but I am not able to spot. Please see sample code as below:
C# class as below:
public class UserData
{
public int ID { get; set; }
public DateTime DATETIME { get; set; }
public int SEQUENCE { get; set; }
}
And then WEB API method (API Controller as below)
[HttpPost()]
public HttpResponseMessage Post([FromBody()]
IEnumerable<#UserData> RequestBody)
{
}
Json array as below
[
{
"ID": 1,
"DATE": "2014-01-01",
"SEQUENCE": 533
},
{
"ID": 2,
"DATE": "2015-01-01",
"SEQUENCE": 3233
},
{
"ID": 3,
"DATE": "2015-01-01",
"SEQUENCE": 233
}
]
And the .NET Client as below:
public void CallService(string jsonString)
{
try {
var client = new RestClient(GetBaseURLService());
var requestRest = new RestRequest("event ", Method.POST);
var RequestBody = TextBoxCreateEventJson.Text;
requestRest.AddBody(jsonString);
requestRest.RequestFormat = DataFormat.Json;
var res = client.Execute(requestRest);
} catch (Exception ex) {
}
}
And then I get null/nothing in the RequestBody.
I know something I need to do before a call to requestRest.AddBody(jsonString);
But not sure what?
In System.Web.Helpers namespace, there is a Json class which you can use in order to encode or decode. For your case,
string jsonString = Json.Encode(your array as an argument)
Use jsonString in the body of your request.

Categories