Calling Generic WebAPI end point from the code - c#

I have an endpoint as shown here in my API controller:
[HttpPost("getdynamictypedbytasync")]
[ProducesResponseType(StatusCodes.Status405MethodNotAllowed)]
public async Task<IActionResult> GetDynamicTypedByTAsync<T>([FromBody] object[] datas) where T : class
{
try
{
var data = await UowLookups.DynamicRepository().GetAllAsyncByT<T>();
return Ok(data);
}
catch (Exception e)
{
}
}
I have the following questions:
Is this the right method?
How will I call this API from an ASP.NET MVC app and pass some parameters?
I tried this code, but I get errors:
Type type = typeof(T);
var property = type.GetProperties();
using (var response = await _httpGatewayClient.PostAsJsonAsync(apiEndpoint, type, _options))

Related

Correct syntax of dynamic return type ActionResult<T> use by explicit ActionResult<Contact> and others

I have the following code, would like to create dynamic return type of ActionResult of function getResponse, so it can be used as a generic function for all other models like ActionResult.ActionResult etc etc.
The reason I am having an explicit return type is because it needs to be working with Swashbuckle openAPI
which response can be in XML, Json, Text...
and it looks like it only works by using ActionResult
What is the correct syntax of public async Task> getResponse(HttpResponseMessage results)??? Thanks, guys.
public async Task<ActionResult<Contact>> Get(string referenceId = "af2f8f37-c1d9-40d3-9f29-08d5dab10621")
{
Settings.ReferenceID = referenceId;
//same as get a single Contact;
var url = Settings.PutContactUrl;
Log.Information($"Endpoint hit: {url}");
var requestData = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(url),
};
var results = await _client.SendAsync(requestData);
return getResponse(results);
if (!results.IsSuccessStatusCode)
return StatusCode((int)results.StatusCode, JsonConvert.DeserializeObject(results.Content.ReadAsStringAsync().Result));
//need to cast type to provide xml result
var contact = JsonConvert.DeserializeObject<Contact>(results.Content.ReadAsStringAsync().Result);
//var abc = _mapper.Map<Contact>(contact);
return StatusCode((int)results.StatusCode, contact);
}
[NonAction]
public async Task<ActionResult<T>> getResponse(HttpResponseMessage results)
{
if (results.IsSuccessStatusCode)
{
switch (results.StatusCode)
{
case HttpStatusCode.NotFound:
return StatusCode((int)results.StatusCode, results.Content.ReadAsStringAsync().Result);
default:
return StatusCode((int)results.StatusCode, JsonConvert.DeserializeObject(results.Content.ReadAsStringAsync().Result));
}
}
return StatusCode((int)results.StatusCode, JsonConvert.DeserializeObject(results.Content.ReadAsStringAsync().Result));
}

Returning multiple values from WebApi (post) to AngularJs

I am using .net core C#, WebApi & AngularJs.
For saving data my Angularjs code makes a $http call to my WebApi. I can return single data from my api fine but not sure whats the best method to return multiple values here. I can make it comma separated and then return as well, but wanted to know if there is a better approach to this.
So basically when the API saves data to my db, I want to return a variable, boolean value if the save was successful and an exception message in case the save was not successfully. Below is my code.
AngularJs Code:
service.saveData(data).then(function (res) {
//get someDataToReturn, dataSaved & exception raised if any from db save here.
}, function (err) {
});
WebApi Code:
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
bool dataSaved = true;
string someDataToReturn = string.Empty;
//do some processing and updating someDataToReturn here
//Saving data to DB
dataSaved = SaveData(data);
//I want to return someDataToReturn, dataSaved(true or false) and exception raised from SaveData if any
return Ok(someDataToReturn);
}
//DB Call to save data
public bool SaveData(List<UserData> data)
{
try
{
foreach (var set in data)
{
//creating query etc
_db.Execute(query);
}
return true;
}
catch (SqlException ex)
{
}
return false;
}
Let me know the best approach for this.
First of, you should check if the values in your request body is correctly populated.
Take a look at DataAnnotations.
You can use annotations to specify which properties in your model that are Required, Min and Maxlength etc.
Here's an example on how to define a Name property to be required on the UserData class
public class UserData
{
[Required]
public string Name { get; set; }
}
If the request model do not fulfill the specified rules set on the UserData class DataAnnotations, the context ModelState will be set to false and contain the DataAnnotations errors.
This can be used to determind if the current request is a bad request and return a proper http status code from that.
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); //will return a 400 code
...
Then regarding the SaveData method. Capture the exception in the controller and return a proper status code from there
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); //400 status code
try
{
SaveData(data);
}
catch(Exception e)
{
return InternalServerError(e); //500 status code
}
string someDataToReturn = string.Empty;
return Ok(someDataToReturn ); //200 status code
}
public void SaveData(List<UserData> data)
{
foreach (var set in data)
{
//creating query etc
_db.Execute(query);
}
}
You can use the Controller class method Json(object data). Something like:
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
return this.Json(SaveData(data));
}
See this.
you can create an entity and return it
public class BaseResult{
public bool Result{get;set;}
public string Errors{get;set;}
}
or only
return Ok( new { result = dataSaved , error= exception.Message});
the standard way is:
return 201 status code
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201
[HttpPost("data/save")]
public async Task<IHttpActionResult> SaveData([FromBody] List<UserData> data)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
// return response of 201 if you created the resource successfully
// typically return this with a uri to the new resource
return Created("location", saveData(data));
}
catch (Exception)
{
return InternalServerError();
}
}

Converted POST FromBody to HttpRequestMessage - Need to Desearialize Body of Request

I have working REST code that accepts POST messages using FromBody. The parameter received is the object I need to work with. Now, I need to examine the authorization in the header. I think have this figured out by switching the parameter from the object to an HttpRequestMessage. Of course, now the content of the message must be converted to the original object and I'm having difficulty figuring it out.
Here is the original method:
[HttpPost]
public IHttpActionResult Post([FromBody] CardStatusRoot cardStatus)
{
try
{
if (cardStatus == null)
{
return BadRequest("Card data not provided");
}
if (cardStatus.Data.TransactionType.ToLower() == "card")
{
//... Process;
}
}
catch (Exception ex)
{
try
{
// Log the failure to fund the card
}
catch { }
return InternalServerError();
}
return Ok();
}
New Code, using HttpRequestMessage:
[HttpPost]
public IHttpActionResult Post(HttpRequestMessage request)
{
// Get the authentication from the header
var encoding = Encoding.GetEncoding("UTF-8");
var authValue = encoding.GetString(Convert.FromBase64String(request.Headers.Authorization.Parameter));
var validAuthorization = ConfigurationManager.AppSettings["ValidKey"];
if (authValue != validAuthorization)
{
return BadRequest("Not Authorized");
}
// This does NOT compile - Need help converting request.Content to a CardStatusRoot object
CardStatusRoot cardStatus = (CardStatusRoot)request.Content.ReadAsStringAsync().Result;
... Same as first method
}
How do I convert the content of the request to a CardStatusRoot object?
ApiController has access to the current request via the Request property.
[HttpPost]
public IHttpActionResult Post([FromBody] CardStatusRoot cardStatus) {
try {
HttpRequestMessage request = this.Request;
if (cardStatus == null) {
return BadRequest("Card data not provided");
}
if (cardStatus.Data.TransactionType.ToLower() == "card") {
//... Process;
}
} catch (Exception ex) {
try {
// Log the failure to fund the card
}
catch { }
return InternalServerError();
}
return Ok();
}
That said, this question feels more like an XY problem.
You should be looking into
Authentication Filters in ASP.NET Web API 2
Global Error Handling in ASP.NET Web API 2
Just use the previous signature method to compute with data. You can directly access headers value like this
Request.Headers.Authorization.Parameter
Where Request is the object provided in ApiController for each request.

passing multiple objects as parameters to mvc 6 action

I have an MVC 5 project that is working fine, and I need to migrate that project to MVC 6 with.net core. After managing to tweak everything to work I got stuck with a problem: Many of my actions accept more than 1 object as a parameter. The model binder MVC 5 is using has no problem with that, but MVC 6 seems to place null in all the parameters of such actions, I guess it's part of the unification of MVC and WebAPI. My question is if there is anyway around it without adding another model library of request wrapper objects.
for example:
[HttpPost]
public ActionResult GetVersionData(OvlEnvironment environment, Pipeline pipeline)
{
BL.SetEnviromentVersion(pipeline, environment);
return PartialView("_Version", environment);
}
On the mvc 5 project ajax requests containing json data in the form
{ "environment" : {*Data...*},
"pipeline" : {*Data...*}
}
were accepted. In mvc 6 both objects in response to the same request appear to be null.
Thanks
You have to give the ASP.NET Core MVC Framework the hint, that data to bind to the model is to be found in the body of the post request. This is done via the [FromBody] attribute.
[FromBody]: Use the configured formatters to bind data from the
request body. The formatter is selected based on content type of the
request.
By design it is not possible to bind multiple parameters to one JSON source as it is described here. In order to avoid additional model classes you can use a generic JObject like this:
[HttpPost]
public ActionResult GetVersionData([FromBody] JObject data)
{
var environment = data["environment"].ToObject<OvlEnvironment>();
var pipline = data["pipeline"].ToObject<Pipeline>();
BL.SetEnviromentVersion(pipeline, environment);
return PartialView("_Version", environment);
}
I know this post is old, but I also ran into this problem.
I think the accepted answer is right, in ASP.NET MVC5 it was possible to seek the HttpRequest.InputStream, but this is not allowed anymore in ASP.NET Core 2. So the work-around could be to read the stream once and store the parsed JObject in the ModelState (https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding.modelstatedictionary?view=aspnetcore-2.1). Simple tests show that this approach could work.
This is an example of such an approach:
public class JsonMultiParameterModelBinder : IModelBinder
{
private static IModelBinder DefaultModelBinder;
private const string JSONKEY = "json_body";
public JsonMultiParameterModelBinder(IModelBinder defaultModelBinder)
{
DefaultModelBinder = defaultModelBinder;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var httpContext = bindingContext.HttpContext;
var stream = bindingContext.HttpContext.Request.Body;
if (httpContext.Request.ContentType.Contains("application/json"))
{
JObject jobject = null;
ModelStateEntry entry = null;
bindingContext.ModelState.TryGetValue(JSONKEY, out entry);
if (entry == null)
{
using (var readStream = new StreamReader(stream, Encoding.UTF8))
using (var jsonReader = new JsonTextReader(readStream))
{
jobject = JObject.Load(jsonReader);
}
bindingContext.ModelState.SetModelValue(JSONKEY, jobject, null);
}
else
{
jobject = entry.RawValue as JObject;
}
var jobj = jobject[bindingContext.FieldName];
if (jobj == null)
{
httpContext.Response.StatusCode = 500; // error has occured
throw new JsonReaderException(string.Format("The rootnode '{0}' is not found in the Json message.", bindingContext.ModelName));
}
object obj = null;
var JSONSerializer = new JsonSerializer();
try
{
obj = jobj.ToObject(bindingContext.ModelType, JSONSerializer);
bindingContext.Model = obj;
bindingContext.Result = ModelBindingResult.Success(obj);
return Task.CompletedTask;
}
catch (Exception ex)
{
httpContext.Response.StatusCode = 500; // error has occured
throw ex;
}
}
return DefaultModelBinder.BindModelAsync(bindingContext);
}
}
And the provider for this binder :
public class JsonMultiParameterModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (context.Metadata.IsComplexType)
{
ComplexTypeModelBinderProvider p = new ComplexTypeModelBinderProvider();
return new JsonMultiParameterModelBinder(p.GetBinder(context));
}
return null;
}
}
Register in Startup.cs:
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new JsonMultiParameterModelBinderProvider());
}

C# Calling WebApi method from MVC controller parameter is always null

I have a situation where I have an MVC Controller that I'm calling a WebApi POST method. I'm passing a DTO as a parameter... The WebApi method is hit, but the parameter is null. What am I missing?
Thanks!
MVC Controller:
[Authorize]
public async Task<ActionResult> Index()
{
Permissions service = new Permissions();
ViewBag.Title = "Deployment Manager";
string uri = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiRestfulUrl"] + "/Permissions";
using (var client = new HttpClient())
{
var permissions = new PermissionsDTO() { UserName = "rherhut" };
var response = await client.PostAsJsonAsync(uri, permissions);
if (response.IsSuccessStatusCode)
{
// Get response data how??
}
}
return View();
}
WebAPI POST Method:
public HttpResponseMessage Post(HttpRequestMessage request, [FromBody]PermissionsDTO permissions)
{
var data = repository.HasAdminRights(permissions.UserName); // permissions.UserName is null
var response = new ApiResponseCreator<PermissionsDTO>();
return response.FormatReturnData(request, data);
}
I resolved the problem by decorating the dto class with a "Serialize" attribute and for some reason this worked.

Categories