Make parameters in swashbuckle optional(not required) - asp.net-core

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

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

DotNet - API - Can't access endpoint

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

How do you manage the visible input fields accepted in an API HttpPost request?

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

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.

Restore AJAX handling for ASP.NET Core to previous functionality

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