DotNet - API - Can't access endpoint - api

I am passing through quite a similar challenge to the one reported on this post - .Net Core API Endpoint not allowing QueryString parameters - but the accepted answer hasn't worked for me, so I am seeking some guidance.
[HttpGet, Route("api/indicators/getindicatorsvalues/{companyId=companyId}/{pathToFile=pathToFile}")]
[ProducesResponseType(typeof(ComputedIndicatorVM), StatusCodes.Status200OK)]
public async Task<IActionResult> GetIndicatorsValues([FromQuery] Guid companyId, [FromQuery] string pathToFile)
{
//code goes here
}
[HttpGet("{id}")]
[ProducesResponseType(typeof(IndicatorDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetByIdAsync(Guid id)
{
//some more code goes here
}
Calling the 1st endpoint:
URL: https://localhost:5001/api/indicators/GetIndicatorsValues?companyId=cTest&pathToFile=ptfTest
Result: {"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|6c8dcccd-412c0e1f0b9eb222.","errors":{"id":["The value 'GetIndicatorsValues' is not valid."]}}
Calling the 2nd endpoint works just fine:
URL: https://localhost:5001/api/indicators/DFAF6EAE-AB4B-4563-B37E-57DEF730A1D7
It seems by the response of the first endpoint that it is considering GetIndicatorsValues as a param for the second endpoint?
Or am I missing something else?

According to your description, it seems this issue is the https://localhost:5001/api/indicators/GetIndicatorsValues will match both the GetIndicatorsValues and GetByIdAsync. I guess your has a default rule which is like {controller}/{action}/{id?}.
I suggest you could try to modify the controller codes to avoid match the same method and then it will work well.
[HttpGet, Route("api/indicators/getindicatorsvalues/{companyId=companyId}/{pathToFile=pathToFile}")]
[ProducesResponseType(typeof(ComputedIndicatorVM), StatusCodes.Status200OK)]
public async Task<IActionResult> GetIndicatorsValues([FromQuery] Guid companyId, [FromQuery] string pathToFile)
{
//code goes here
}
[HttpGet("api/indicators/getindicatorsvalues/GetByIdAsync/{id}")]
[ProducesResponseType(typeof(IndicatorDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetByIdAsync(Guid id)
{
//some more code goes here
}

Sorted:
[HttpGet]
[ProducesResponseType(typeof(IndicatorDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetByIdAsync(Guid id)
{
var indicator = await this.indicatorsService.GetByIdAsync(id);
if (indicator == null)
{
return NotFound();
}
return Ok(indicator);
}
[HttpGet("GetIndicatorsValues")]
[ProducesResponseType(typeof(ComputedIndicatorVM), StatusCodes.Status200OK)]
public async Task<IActionResult> GetIndicatorsValues([FromQuery] Guid companyId, [FromQuery] string pathToFile)
{
try
{
if (companyId!=default && !string.IsNullOrEmpty(pathToFile))
{
List<ComputedIndicatorVM> computedIndicatorsViewModel = new List<ComputedIndicatorVM>();
List<ComputedIndicatorDto> dtoList = await this.indicatorsService.CalculateIndicators(companyId, pathToFile);
computedIndicatorsViewModel = this.mapper.Map<List<ComputedIndicatorVM>>(dtoList);
return Ok(computedIndicatorsViewModel);
}
else
{
return NotFound();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
throw ex;
}
}

Related

When send Parameter pagenumber to web api by post man action not working or recognize?

I work on .net core 6 web api I face issue when
send paramter pagenumber on url by post man
action not catched by break point debug .
so what is issue and How to solve It ?
i try with url
https://localhost:7235/api/items/pageNumber=1
[HttpGet("{pageNumber}")]
public async Task<IActionResult> GetAll(int pageNumber)
{
}
it return error 200 bad Request .
but i try with url below
https://localhost:7235/api/items/
[HttpGet]
[Route("")]
public async Task<IActionResult> GetAll(int pageNumber)
{
}
it working hit controller success and return data success fromitems controller action get all .
so What is issue please and How to solve it ?
Updated post not working
[HttpGet("{pageNumber}")]
public async Task<IActionResult> GetAll(int pageNumber)
{
}
for this action you need to use this url:
https://localhost:7235/api/items/1
not working and it give me error 500 internal server error
all controller items
using DomainModel.Entities;
using DomainModel.Pagination;
using DomainModel.ViewModel;
using k8s.Models;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Service.Contract;
using Service.Features.CustomerFeatures.Commands;
using Service.Features.CustomerFeatures.Queries;
namespace WebApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ItemsController : Controller
{
private readonly IitemService _iitem;
private readonly IPageHelper<ItemsViewModel> _pageHelper;
public ItemsController(IitemService iitem, IPageHelper<ItemsViewModel> pageHelper)
{
_iitem = iitem;
_pageHelper = pageHelper;
}
[HttpPost]
public async Task<IActionResult> Create(Items item)
{
await _iitem.AddAsync(item);
return Ok();
}
[HttpGet("{pageNumber}")]
public async Task<IActionResult> GetAll(int pageNumber)
{
var allitems = _iitem.GetAllItems();
var result = _pageHelper.GetPage(allitems.AsQueryable(), pageNumber);
var itemsdata = new ItemsPageViewModel
{
items = result.Items,
Pager = result.Pager
};
return Ok(itemsdata);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetById(int id)
{
var details = await _iitem.GetByIdAsync(id);
return Ok(details);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
await _iitem.DeleteAsync(id);
return Ok();
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, Items item)
{
if (id != item.Id)
{
return BadRequest();
}
await _iitem.UpdateAsync(id, item);
return Ok();
}
}
}
The error you are actually getting is
AmbiguousMatchException: The request matched multiple endpoints.
This is because you have two actions that have the same route:
// for this action you need to use this url:
// https://localhost:7235/api/items/1
[HttpGet("{pageNumber}")]
public async Task<IActionResult> GetAll(int pageNumber)
{
var allitems = _iitem.GetAllItems();
var result = _pageHelper.GetPage(allitems.AsQueryable(), pageNumber);
var itemsdata = new ItemsPageViewModel
{
items = result.Items,
Pager = result.Pager
};
return Ok(itemsdata);
}
// for this action you ALSO need to use this url:
// https://localhost:7235/api/items/1
[HttpGet("{id}")]
public async Task<IActionResult> GetById(int id)
{
var details = await _iitem.GetByIdAsync(id);
return Ok(details);
}
Both actions are HttpGet and both use the same route:
https://localhost:7235/api/items/x
you need to change the route for one of them.
Routing to controller actions in ASP.NET Core

Sent value from an action to another action

How to pass the parameter from the first method to the second?
I want to use id in public async Task<IActionResult> CreateReport.
public IActionResult CreateReport(int id)
{
return View();
}
// POST: MemeReports/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateReport([Bind("Id_report,Id_user,Id_meme,Description")] MemeReports memeReports)
{
memeReports.id_meme=id //i want do this
if (ModelState.IsValid)
{
db.Add(memeReports);
await db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(memeReports);
}
Simply pass it in your post method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateReport(int id, [Bind("Id_report,Id_user,Id_meme,Description")] MemeReports memeReports)
{
memeReports.id_meme=id //i want do this
if (ModelState.IsValid)
{
db.Add(memeReports);
await db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(memeReports);
}
Your first method gets called if the site yourdomain/createreport/1 (or yourdomain/createreport?id=1) is called and shows the createReport View.
If you submit a post from that page you will post the id, too.
You can use TempData which stores data until it's read :
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-2.1#tempdata
In ConfigureServices function :
services.Configure<CookieTempDataProviderOptions>(options =>
{
options.Cookie.IsEssential = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddSessionStateTempDataProvider();
services.AddSession();
In Configure function :
app.UseCookiePolicy();
app.UseSession();
Then in CreateReport function :
public IActionResult CreateReport(int id)
{
TempData["id"] = 1;
return View();
}
You can read this value in post function :
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateReport([Bind("Id_report,Id_user,Id_meme,Description")] MemeReports memeReports)
{
var value = TempData != null ? (TempData["id"]?.ToString() ?? "No TempData Value") : "No TempData";
memeReports.id_meme= value;
if (ModelState.IsValid)
{
db.Add(memeReports);
await db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(memeReports);
}
TempData should work after the user accepts the cookie policy on the site.

Make parameters in swashbuckle optional(not required)

I want to make param in my controller as optional but swagger shows it as required.
my controller looks like:
[HttpGet("{name}")]
[SwaggerResponse((int)HttpStatusCode.OK)]
[SwaggerResponse((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetPolicy(string name)
{
if (string.IsNullOrEmpty(name))
{
return BadRequest("Name is empty");
}
try
{
CRUDPolicyResponse crudPolicyResponse = await _managementOperations.CRUDPolicy(CRUDType.Read, name, null);
if (!crudPolicyResponse.OperationSucceeded)
{
return StatusCode((int)HttpStatusCode.BadRequest, crudPolicyResponse.Message);
}
if (crudPolicyResponse.FileMetadataPolicy == null)
{
return NotFound($"Policy name doesn't exists NAME: {name}");
}
return Ok(crudPolicyResponse.FileMetadataPolicy);
}
catch (Exception ex)
{
_log.Error("Error while trying to save file meta data policy", ex);
return StatusCode((int)HttpStatusCode.InternalServerError, ex);
}
}
I tried to change to default value like this: string name = null but not working/
So the name string is required and i can't make get with name as empty.
I tried to solve my problem with this solution
make int as nullable
Add the default value to your controller parameter
[HttpGet("{name}")]
[SwaggerResponse((int)HttpStatusCode.OK)]
[SwaggerResponse((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetPolicy(string name = "")
{
...
}
Change your param using FromQuery like this :
public async Task<IActionResult> GetPolicy([FromQuery] string name)
Try a DefaultValue attribute.
In your case, that would be:
[DefaultValue("")]
I recently had this issue, myself, and I resolved it by adding a second endpoint:
Your original signature:
[HttpGet("{name}")]
[SwaggerResponse((int)HttpStatusCode.OK)]
[SwaggerResponse((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetPolicy(string name)
and the new one:
[HttpGet("")]
[SwaggerResponse((int)HttpStatusCode.OK)]
[SwaggerResponse((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetPolicy()
Naturally, this led to refactoring the body of the method, and the two endpoints became arrow methods:
protected async Task<CRUDPolicyResponse> GetPolicy(string name = null) {
...
}
[HttpGet("{name}")]
[SwaggerResponse((int)HttpStatusCode.OK)]
[SwaggerResponse((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetPolicyByName(string name) => OK(await GetPolicy(name));
[HttpGet("")]
[SwaggerResponse((int)HttpStatusCode.OK)]
[SwaggerResponse((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> GetPolicyAnyPolicy() => OK(await GetPolicy());

IdentityResult to ActionResult

Is there an easy way to convert an IdentityResult to an IActionResult taking into account errors?
IdentityResult is just a class which tells you if an ASP.NET (Core) Identity operation succeeded or not and in case of an error offers you error messages and is unrelated to MVC Action's results which implement IActionResult interface.
If you use WebApi/RestApi controllers , you'd translate it to something like
public IActionResult SomeActionName()
{
IdentityResult result = ...; // some identity operation
// all is okay, return http code 200
if(result.Success)
return Ok();
// error happened, return http code 400 + errors as json
return BadRequest(result.Errors);
}
Or if you are really lazy, write your own IActionResult:
public class IdentityActionResult : IActionResult
{
private readonly IdentityResult identityResult;
public IdentityActionResult(IdentityResult identityResult)
{
this.identityResult = identityResult;
}
public Task ExecuteResultAsync(ActionContext context)
{
IActionResult actionResult = null;
if(identityResult.Success)
{
actionResult = new OkResult();
}
else
{
actionResult = new BadRequestObjectResult(identityResult.Errors);
}
return actionResult.Execute(context);
}
}
Of course this can be further optimized so that you don't have to create two IActionResult objects per request, but that's an exercise left up to you ;)
You can write an extension method for IdentityResult which returns ObjectResult something like this:
public static class IdentityResultExtension
{
public static ObjectResult ToObjectResult(this IdentityResult result)
{
//
return new ObjectResult(result);
}
}
then use it in action:
public IActionResult YourAction()
{
IdentityResult result;
return result.ToObjectResult();
}

WebApi for Email delivery

I created a Web API controller inside my ASP.NET MVC 4 Website to specifically send emails so that I can consume it for this purpose and eventually provide it to other consumers (Mobile, etc).
Everything is working fine but I want to use it asynchronously, altough it's not working that way, my website blocks until the work is finished.
I have a regular Controller where I call a helper class that calls the API.
Controller Code :
[HttpPost]
public async Task<ActionResult> ContactUs(ContactUsModel model)
{
ExternalApiCalls extApi = new ExternalApiCalls();
await extApi.PostContactUs(model).ConfigureAwait(false);
return RedirectToAction("ContactUsSuccess", "Account");
}
Helper Class Code :
public class ExternalApiCalls
{
public HttpClient client { get; set; }
public ExternalApiCalls()
{
client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:10239/");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task PostContactUs(ContactUsModel model)
{
try
{
var response = await client.PostAsJsonAsync("api/WAPIMail", model).ConfigureAwait(false);
}
catch (HttpRequestException ex)
{
}
catch (System.FormatException)
{
}
finally
{
}
}
}
Web API Controller :
public class WAPIMailController : ApiController
{
public void PostContactUs(ContactUsModel model)
{
// Send Email Here
}
}
Thanks a lot for your help
I finally managed how to do it..
For brevity's sake, I am showing oversimplified code, see below:
Controller :
[HttpPost]
public ActionResult ContactUs(ContactUsModel model)
{
new ExternalApiCalls().MailContactUs(model);
return RedirectToAction("ContactUsSuccess", "Account");
}
Helper Class :
public void MailContactUs(ContactUsModel model)
{
client.PostAsJsonAsync("api/WAPIMail/MailContactUs", model).ConfigureAwait(false);
}
Web API Controller :
[HttpPost]
public void MailContactUs(ContactUsModel model)
{
//Email Logic Here
}