AspCore Multiple Post Actions with Same Action Name - asp.net-core

I have a checkout page that has multiple methods for payment. Each method has its own partial view containing its own model. I am trying to get keep the url the same on each of the different methods so if there is an error the url doesn't change. Is there a way to achieve this? Thank you for any help, I have been mulling over this for a while now.
CheckOut Model
public class CheckoutForm
{
public Method1Form method1Form { get; set; }
public Method2Form method2Form { get; set; }
public Method3Form method3Form { get; set; }
}
CheckOut Controller
[HttpGet]
[Route("checkout/{guid}")]
public IActionResult Checkout([FromRoute] String guid)
{
....
return View(model);
}
[HttpPost]
[Route("checkout/{guid}")]
public IActionResult Checkout([FromRoute] String guid, Method1 model)
{
....
//Some Error Condition Triggered
return View(checkoutmodel);
}
[HttpPost]
[Route("checkout/{guid}")]
public IActionResult Checkout([FromRoute] String guid, Method2 model)
{
....
//Some Error Condition Triggered
return View(checkoutmodel);
}
[HttpPost]
[Route("checkout/{guid}")]
public IActionResult Checkout([FromRoute] String guid, Method3 model)
{
....
//Some Error Condition Triggered
return View(checkoutmodel);
}
Similar Question without an answer https://stackoverflow.com/questions/42644136

You cannot. There is no way for Route Engine to differentiate those 3 post methods.
You could append something at the end to the URL to make them different.
[HttpPost]
[Route("checkout/{guid}/paypal")]
public IActionResult Checkout([FromRoute] String guid, Method1 model)
{
....
}
[HttpPost]
[Route("checkout/{guid}/authorizenet")]
public IActionResult Checkout([FromRoute] String guid, Method2 model)
{
....
}

Related

how to send array to API which contains image and other data in .net core

When I am passing a single object like below then it is working as per below image
[HttpPost]
public async Task<ActionResult> Post([FromForm] MyModel Details)
{
}
but when I am passing the List of the object to API then it is not working. option to upload a file is not visible. and if I entered any values in the array then also I am getting count 0 for details.
[HttpPost]
public async Task<ActionResult> Post([FromForm] List<MyModel> Details)
{}
I want to pass the List of images and descriptions to API. How can I achieve it?
Thanks in advance!
You need custom model binding for the list model . Here is a similar demo:
custom model binding code:
public class MetadataValueModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (values.Length == 0)
return Task.CompletedTask;
var options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };
var deserialized = JsonSerializer.Deserialize(values.FirstValue, bindingContext.ModelType, options);
bindingContext.Result = ModelBindingResult.Success(deserialized);
return Task.CompletedTask;
}
}
Add the model binder to the model class:
public class MasterDTO
{
public string Comments { get; set; }
public IFormFile File { get; set; }
public List<DetailDTO> Details { get; set; }
public MasterDTO()
{
this.Details = new List<DetailDTO>();
}
}
[ModelBinder(BinderType = typeof(MetadataValueModelBinder))]
public class DetailDTO
{
public Int64 ElementId { get; set; }
public double LowerLimit { get; set; }
public double HigherLimit { get; set; }
public string Status { get; set; }
public string UserAuthorization { get; set; }
public DateTime? AutorizationDate { get; set; }
}
controller/action
[HttpPost]
public async Task<IActionResult> CreateProjectLimit([FromForm] MasterDTO masterDto)
{
//...
return Ok();
}
You can just use postman to pass the list of images and Descriptions to API
Below is the right answer. we can use Postman to pass images in the array as shown below.

Asp.Net Core - multiple action methods with the same name and different parameters

I'm looking for a way to have more than one action method with the same name in controller without changing Url (route).
[HTTPPost]
Public ActionResult Method1 (Dto1 param)
{
}
[HTTPPost]
Public ActionResult Method2 (Dto2 param)
{
}
[HTTPPost]
Public ActionResult Method3 (Dto3 param)
{
}
This throws error -
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints
Dto1, Dto2 and Dto3 derive from a base Dto, each have properties specific to different request methods. I am trying to avoid having a single method with a common Dto which will require multiple validations such as validating mandatory fields based on the value of other fields, etc. If we can have 3 different Post methods with different Dtos, it would makes things much easier
Adding Dtos (Simplified)
public class BaseDto
{
public string CommonProp1 { get; set; }
public string CommonProp2 { get; set; }
}
public class Dto1: BaseDto
{
public enumType Type = enumType.Type1;
public string Property1 { get; set; }
}
public class Dto2 : BaseDto
{
public enumType Type = enumType.Type2;
public string Property2 { get; set; }
}
public class Dto3 : BaseDto
{
public enumType Type = enumType.Type3;
public string Property3 { get; set; }
}
You can use Routes or calling a private method from the three above methods, you shouldn't do this as you want. I think your problem is more deep.
But.... if you still want it, here is a workaround.
Instead of receiving an object, receive a string with json content and parse the object.
But you will have to have a property inside the "json object" or another parameter that defines you wich object it is (Dto1, Dto2 or Dto3). In any case will be the same that use different routes or methods because objects are different.
[HTTPPost]
Public ActionResult Method (string param)
{
//Decode your string called param with JSON with a property inside
}
or
[HTTPPost]
Public ActionResult Method (string param, int type)
{
//Decode your string called param with JSON switching "type" as 1, 2 or 3
}
UPDATE after your update:
I suggest you receive BaseDto and the type in other parameter.
[HTTPPost]
Public ActionResult Method (BaseDto param, int type)
{
}

Ajax call, where parameter is IRequest (Mediatr), has null in all properties

Here is my controller...
public class AccountController : BaseController
{
[Route("Account/json_account_log_in")]
public async Task<JsonResult> json_account_log_in(ValidateUserQuery query)
{
ValidateUserDto response = await Mediator.Send(query);
// Do stuf...
}
}
And here is the query class..
public class ValidateUserQuery : IRequest<ValidateUserDto>
{
public string Username { get; set; }
public string Password { get; set; }
}
But in my controller, the query Username and Password are null.
If I remove the IRequest<ValidateUserDto> then Username and Password are correct - but then I can't use Mediatr.
Can't I use classes that inherits from IRequest in ajax calls queries?
Solution ... I needed to add [FromBody]:
public async Task<JsonResult> json_account_log_in([FromBody] ValidateUserQuery query)

Upload Collection of ViewModels containing IFormFile in Asp.Net Core 2.1

I have a document service API written in ASP.NET Core Web API 2.1. This API currently accepts a ViewModel containing information for 1 file and then saves that file and information to the server. I want to add the ability to add multiple files, but I cannot get the IFormFile file to bind when uploaded as part of a collection.
/// The ViewModel being uploaded.
public class FileUploadDto
{
public Guid Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public IFormFile File { get; set; }
}
/// Works perfect. All properties are bound to the fileUploadRequest object.
[HttpPost]
public async Task<IActionResult> Add(FileUploadDto fileUploadRequest)
{
/// Process
return Ok();
}
/// All properties are bound to the objects in the collection, except for the File property.
[HttpPost]
public async Task<IActionResult> AddRange([FromForm] IEnumerable<FileUploadDto> fileUploadRequests)
{
foreach (FileUploadDto fileUploadRequest in fileUploadRequests)
{
//fileUploadRequest.File is null
}
return Ok();
}
I am testing this by using Postman, and uploading the content in the body, using form-data, as:
[0][Description]:Test (Type: Text)
[0][Name]:Test (Type: Text)
[0][File]:sample.txt (Type: File)
multipart/form-data is set in the content-type header.
UPDATE:
I've found a workaround by doing the following, but obviously this is not the best thing to do. I recognize the problem is that the files are not being bound to my ViewModels, but are being stored in the Request.Form.Files collection. I'm not sure how to work around that, though.
[HttpPost]
public async Task<IActionResult> AddRange([FromForm] IEnumerable<FileUploadDto> fileUploadRequests)
{
var index = 0;
foreach (FileUploadDto fileUploadRequest in fileUploadRequests)
{
//fileUploadRequest.File is null
fileUploadRequest.File = Request.Form.Files[index];
index += 1;
}
return Ok();
}
I ended up passing an additional property to specify the file index to use in the Request Form.
/// The ViewModel being uploaded.
public class FileUploadDto
{
public Guid Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public int? FileIndex { get; set; }
public IFormFile File { get; set; }
}
/// Works perfect. All properties are bound to the fileUploadRequest object.
[HttpPost]
public async Task<IActionResult> Add(FileUploadDto fileUploadRequest)
{
/// Process
return Ok();
}
/// All properties are bound to the objects in the collection, except for the File property.
[HttpPost]
public async Task<IActionResult> AddRange([FromForm] IEnumerable<FileUploadDto> fileUploadRequests)
{
foreach (FileUploadDto fileUploadRequest in fileUploadRequests)
{
fileUploadRequest.File = Request.Form.Files[fileUploadRequest.FileIndex ?? -1];
}
return Ok();
}

Conflict Variable Value in Get/POST

I have created a asp.net mvc application which has Get and Post method like
public ActionResult MyData(string companyid)
{
MyModel model= new MyModel ();
model = (LINQ) ;
return View(model);
}
[HttpPost]
public ActionResult MyData(MyModel model)
{
if(model.companyid>0)
{
//Update Database
}
else
{
// insert database
}
return View(model);
}
public class MyModel
{
public int companyid {set; get;}
public string Name {set; get;}
public string Address {set; get;}
}
Now my question is: I am getting model.companyid =0 always in post i.e. # if(model.companyid>0)
when I update the get method
from public ActionResult MyData(string companyid)
to public ActionResult MyData(string cid)
it works.
i.e. rename companyid to cid, why it happens. Please let me know...I think, we have seperate method for get and post.