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());
Related
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
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;
}
}
In my API I have a Create method in my controller that accepts all of the models fields, but in the method I'm excluding the ID field since on a create it's generated. But in Swagger it's showing the following.
Is there a way for it not to show the following part?
"id": 0
Is a viewmodel how I should go about this?
I tried the following, but can't get it to work.
public class PartVM
{
public string Name { get; set; }
}
public interface IPartService
{
Task<Part> CreatePart(PartVM part);
Task<IEnumerable<Part>> GetParts();
Task<Part> GetPart(int partId);
}
public class PartService : IPartService
{
private readonly AppDbContext _appDbContext;
public PartService(AppDbContext appDbContext)
{
_appDbContext = appDbContext;
}
public async Task<Part> CreatePart(PartVM part)
{
var _part = new Part()
{
Name = part.Name
};
var result = await _appDbContext.Parts.AddAsync(_part);
await _appDbContext.SaveChangesAsync();
return result.Entity;
}
}
Here's my controller.
[Route("api/[controller]")]
[ApiController]
public class PartsController : ControllerBase
{
private readonly IPartService _partService;
public PartsController(IPartService partService)
{
_partService = partService;
}
[HttpPost]
public async Task<ActionResult<Part>> CreatePart(PartVM part)
{
try
{
if (part == null)
return BadRequest();
var _part = new Part()
{
Name = part.Name
};
var createdPart = await _partService.CreatePart(_part);
return CreatedAtAction(nameof(GetPart),
new { id = createdPart.Id}, createdPart);
}
catch (Exception /*ex*/)
{
return StatusCode(StatusCodes.Status500InternalServerError, "Error creating new record in the database");
}
}
I'm getting a build error saying "CS1503 Argument 1: cannot convert from 'MusicManager.Shared.Part' to 'MusicManager.Server.Data.ViewModels.PartVM'".
It's refering to "_part" in this line "var createdPart = await _partService.CreatePart(_part);".
Any help is appreciated, thank you!
you have a CreatePart method which receives a PartVM model, but you are sending a Part Model to it
change your method to this :
public async Task<Part> CreatePart(Part part)
{
var result = await _appDbContext.Parts.AddAsync(_part);
await _appDbContext.SaveChangesAsync();
return result.Entity;
}
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.
In previous MVC5 and below, you could make an ajax call that unwrapped the parameters properly:
JS:
$.post('/controller/endpoint',{intparam: 1, strparam: 'hello'})
CS:
public ActionResult endpoint(int intparam, string strparam){}
In the new aspnetcore, it has changed:
CS:
public CustomClassWrapper{
public int intparam {get;set;}
public string stringparam {get;set;}
}
public ActionResult endpoint([FromBody]CustomClassWrapper item){}
To sum it up, in the new framework, you need to write a wrapper class and can only pass one [FromBody] parameter to the method. Previously, the params would be unwrapped by variable name correctly.
So, i'm trying to re-implement this functionality in an aspnetcore middleware component. I'm having difficulty in how to accomplish calling the controller method properly with the parameters.
My current cut-down code:
public async Task Invoke(HttpContext context)
{
if (IsAjaxRequest(context.Request))
{
try
{
string bodyContent = new StreamReader(context.Request.Body).ReadToEnd();
var parameters = JsonConvert.DeserializeObject(bodyContent);
///What to do here?
}
catch (Exception ex)
{
throw new Exception("AJAX method not found ", ex);
}
}
else
{
await _next(context);
}
}
I'm really just not sure about what to do after deserializing the parameters. I have the URL for the endpoint and also the params correctly. Just need to know how to call the method and return the result as JSON. Should i be using Reflection to get the controller method? Or is there a better way using MVC?
Try implement custom IModelBinder.
public class BodyFieldModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
bindingContext.HttpContext.Request.EnableRewind(); // required to read request body multiple times
var inputStream = bindingContext.HttpContext.Request.Body;
if (inputStream.Position != 0L)
inputStream.Position = 0;
var bodyValue = new StreamReader(inputStream, Encoding.UTF8).ReadToEnd();
var jsonObject = (JObject)JsonConvert.DeserializeObject<object>(bodyValue);
if (jsonObject.TryGetValue(bindingContext.FieldName, out var jToken))
{
var jsonSerializer = JsonSerializer.Create();
var result = jToken.ToObject(bindingContext.ModelType, jsonSerializer);
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
bindingContext.Result = ModelBindingResult.Failed();
return Task.CompletedTask;
}
}
Be careful, the code above lacks error handling and etc.
And use it like this:
[HttpPost]
public IActionResult Endpoint([ModelBinder(typeof(BodyFieldModelBinder))] int intparam)
Also you could implement custom attribute to reduce complexity of declaration:
public class BodyFieldAttribute : ModelBinderAttribute
{
public BodyFieldAttribute()
: base(typeof(BodyFieldModelBinder))
{
}
}
it's very simple thing i don't know why it not working at your end
JS
$.post('actionMethodURl', { FirstName: '1', LastName: 'hello' }).done(Successfunction);
CS
[HttpPost]
public ActionResult endpoint(string FirstName,string LastName)
{
object Message = string.Empty;
if (ModelState.IsValid)
{
Message = "Pass";
}
else
{
Message = ModelState.Errors();
}
return Json(Message);
}