Cannot generate swagger with multiple HttpPut commands - odata

I can't get swashbuckle to generate the swagger.json when I have more than one HttpPut command. I am using OData4 and ASP.NET Core 2.2. If I comment one of either of the two HttpPut methods, it generates just fine. I have tried adding [SwaggerOperation(OperationId = '')] attribution and that was unsuccessful. I have tried adding [ODataRoute(nameof())] attribution and that was unsuccessful. I have tried editing the [HttpPut(nameof())] attribute and it didn't work. I am at my wits end trying to figure out the key combination to allow this to work. Any suggestions are eagerly welcomed.
[ApiVersion("1.0")]
[ODataRoutePrefix("Items")]
public class ItemsController : ODataController {
[HttpGet]
[Produces("application/json")]
[ProducesResponseType(typeof(IQueryable<Item>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[EnableQuery(AllowedQueryOptions = Select | OrderBy | Top | Skip | Count,
AllowedFunctions = AllowedFunctions.None,
AllowedArithmeticOperators = AllowedArithmeticOperators.None,
AllowedLogicalOperators = AllowedLogicalOperators.None,
AllowedOrderByProperties = "name,location",
MaxOrderByNodeCount = 2,
MaxTop = 100)]
public async Task<IActionResult> Get() {
...
}
[HttpGet]
[ODataRoute("({key})")]
[ProducesResponseType(typeof(Item), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> GetById(string key) {
...
}
[HttpPost]
[ODataRoute]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Post([FromBody] AddItemCommand command) {
...
}
[HttpPut]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> StopMonitoring([FromBody] StopMonitoringCommand command) {
...
}
[HttpPut]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> ChangeMonitoring([FromBody] ChangeMonitoringCommand command) {
...
}
}
With both HttpPut methods in there, it fails with an "Fetch error undefined /swagger/v1/swagger.json" error message.

#HelderSepulveda and the Actions and Functions in OData v4 Using ASP.NET Web API 2.2 gave me the clues to resolve this issue.
Regardless of whether they are HttpPut or HttpPost on the controller, actions can be added by adding them to both the controller and OData model configuration.
e.g.
public class ODataModelConfiguration : IModelConfiguration {
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion) {
builder.EntitySet<Item>("Items");
builder.EntityType<Item>()
.Action("StopMonitoring")
.Parameter<StopMonitoringCommand>("command");
builder.EntityType<Item>()
.Action("ChangeMonitoring")
.Parameter<ChangeMonitoringCommand>("command");
}
}
and in the controller:
[HttpPut]
[ODataRoute("StopMonitoring")]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> StopMonitoring([FromBody] StopMonitoringCommand command) {
...
}
[HttpPut]
[ODataRoute("ChangeMonitoring")]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> ChangeMonitoring([FromBody] ChangeMonitoringCommand command) {
...
}
Combined with the code in the original post, this generates the following in swagger:
GET /api/Items
POST /api/Items
GET /api/items({key})
PUT /api/items/StopMonitoring
PUT /api/items/ChangeMonitoring

Related

Add 2 ActionResult DeleteConfirmed in 1 controller

Is it possible to have 2 ActionResult DeleteConfirmed in 1 controller?
I have 2 different views that I want to delete.
Thanks,
EB
[HttpPost, ActionName("DeleteLink")]
public ActionResult DeleteConfirmed(int id)
{
Link link = db.Links.Find(id);
db.Links.Remove(link);
db.SaveChanges();
return RedirectToAction("OutOfBank");
}
It seems that you want to overload DeleteConfirmed action. You can use the attribute if you want your code to do overloading.
[ActionName("MyOverloadedName")]
But, you'll have to use a different action name for the same http method.
So it's just semantics at that point. Would you rather have the name in your code or your attribute?
Code Example:
public class HomeController : Controller
{
public ActionResult GetEmpName()
{
return Content("This is the test Message");
}
[ActionName("GetEmpWithCode")]
public ActionResult GetEmpName(string EmpCode)
{
return Content("This is the test Messagewith Overloaded");
}
}
Phil has an article related to this: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

I want to show details of any record using generic repository pattern

the Irepository class in which interface have
void details(int id);
the code for repository class is :
public void details(int id) {
T st = db.Set<T>().Find(id);
}
and the home controller
[HttpPost]
public ActionResult details(int id)
{
return View(re.details(id));
}
it is giving me the error of cannot convert from void to object
if do this in home controller
[HttpPost]
public ActionResult details(int id)
{
var abc = re.details(id);
return View(abc);
}
Cannot assign void to an implicitly-typed variable AutoText1
the logic of having the details function to return void isn`t understandable for me and to solve that
as refereed by the exception you need to have a return type and return the T object from the repository to the api then pass the object to the view.
plus the view model should expect to receive an object of any type then handle showing the details maybe dynamically based on the object properties

AllowAnonymous attribute in SignalR

[Authorize] // Derived from Microsoft.AspNet.SignalR.Authorize
public class ChatHub : Hub
{
[System.Web.Mvc.AllowAnonymous] // [AllowAnonymous] doesn't be supported here
public async Task GetEmails()
{
using (var db = new MyDbContext())
{
var users = await db.Users.ToListAsync();
var emails = new List<string>();
users.ForEach(x => emails.Add(x.Email));
Clients.All.getEmails(JsonConvert.SerializeObject(emails));
}
}
}
My problem: I'd set breakpoint to GetEmails method but it's never hit to. Then, I signed in and tried again, it's working.
That means: [System.Web.Mvc.AllowAnonymous] doen't work within [Microsoft.AspNet.SignalR.Authorize].
So, my question is: Why doesn't namespace Microsoft.AspNet.SignalR support [AllowAnonymous] attribute? And do I need to declare AllowAnonymous attribute for that?
I've solved the problem: Using [System.Web.Mvc.Authorize] instead of [Authorize]. Like this:
[System.Web.Mvc.Authorize]
public class ChatHub : Hub
{
[System.Web.Mvc.AllowAnonymous]
public async Task GetEmails()
{
// ...
}
}
I don't understand why but it should work perfectly :)

Clarification of ASP.NET MVC routing and model binding

In ASP.NET MVC, I have recently found out that:
This doesn't work and results in an HTTP 404.
public class TestController : Controller
{
[HttpGet]
[HttpPost]
public ActionResult Index(TestModel model)
{
return View(model);
}
}
This works fine.
public class TestController : Controller
{
public ActionResult Index(TestModel model)
{
return View(model);
}
}
This also works fine:
public class TestController : Controller
{
[HttpGet]
[ActionName("Index")]
public ActionResult GetIndex(TestModel model)
{
return View("Index", model);
}
[HttpPost]
[ActionName("Index")]
public ActionResult PostIndex(TestModel model)
{
return View("Index", model);
}
}
I would like an explanation of why the first version doesn't work, but the other two do work. I would also appreciate if someone could tell me how I can modify the first version to make it work. I like the first version because it is more concise (1 method rather than 2) and also filters out unnecessary HTTP methods.
HTTP verb attributes are mutually exclusive, a request cannot be both GET and POST at the same time. Instead, you have to do this:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Index(TestModel model) { ... }

asp.net mvc controller with multiple lists and "return to list" link

If you have 5 list views in a controller and in each list you can go to edit, details or delete.
On the edit, details and delete page youo have a link 'return to list'.
What's the best method to 'remember' to which list action you must return?
As a solution I've put some info like CurrentAction in the ViewModel and used that in the View. But if you want to use this with different controllers instead of one...
(You can use a Currentcontroller, CurrentArea, but that's not a 'beautifull' solution)
public class MyController : Controller
{
public ActionResult Index()
{
...
}
public ActionResult List2()
{
...
}
public ActionResult List3()
{
...
}
public ActionResult List4()
{
...
}
public ActionResult Create(...)
{
...
}
[HttpPost]
public ActionResult Create(...)
{
...
}
public ActionResult Edit(...)
{
...
}
[HttpPost]
public ActionResult Edit(...)
{
...
}
public ActionResult Delete(...)
{
...
}
[HttpPost]
public ActionResult Delete(...)
{
...
}
}
thanks
Filip
You can use Request.UrlReferrer Property to examine from where did user come to delete ot edit screens.Then bind url to return to list command.
You can set TempData["ReturnUrl"] in your caller action and then use it to set the url of return to list hyperlink.

Resources