4

i have an object "ApplicantDetail" with list of objects in ApplicantController and i want to send Post Request to Personaldetails Api On To save into database and get response back

Api On ApplicantController

    // POST: api/ApplicantDetail
    [HttpPost]
    [Route("~/api/ApplicantDetail")]
    public IActionResult Post([FromBody] Personaldetail ApplicantDetail)
    {

        Personaldetail personaldetail = new Personaldetail
        {
            Name = ApplicantDetail.Name,
            Cnic = ApplicantDetail.Cnic,
            Phone = ApplicantDetail.Phone
        };

        List<Address> addresses = new List<Address>();
        List<Employee> employees = new List<Employee>();
        List<Bank> banks = new List<Bank>();

        addresses.AddRange(ApplicantDetail.Address);
        employees.AddRange(ApplicantDetail.Employee);
        banks.AddRange(ApplicantDetail.Bank);

        var response = *//How to Call Personaldetail Post APi of PersonaldetailController Controller From Here and Get 
          response back//*

        return null;
    }

Api On Personaldetail Controller

    // POST: api/Personaldetails
    [HttpPost]
    public async Task<IActionResult> PostPersonaldetail([FromBody] Personaldetail personaldetail)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _context.Personaldetail.Add(personaldetail);
        await _context.SaveChangesAsync();

        return CreatedAtAction("GetPersonaldetail", new { id = personaldetail.ApplicantId }, personaldetail);
    }
Stefan
  • 17,448
  • 11
  • 60
  • 79
Alpha977
  • 53
  • 1
  • 2
  • 11
  • 6
    Better to create isolation between the controller and the business logic. When you do that, you can easily reuse a business method and enjoy a good separation. – Amir Popovich Oct 22 '19 at 12:19

4 Answers4

5

You should create a service class to represent the personal details API. In this class, you would inject HttpClient, and then set up IHttpClientFactory with a strongly-typed client:

public class PersonalDetailsService
{
    private readonly HttpClient _client;

    public PersonalDetailsService(HttpClient client)
    {
        _client = client;
    }

    // methods here to interact with API via `_client`
}

Then, in ConfigureServices:

services.AddHttpClient<PersonalDetailsService>(c =>
{
    c.BaseAddress = new Uri(Configuration["PersonalDetailsAPIUrl"]);
    // configure anything else you need to on the `HttpClient`
});

Finally, in your controller, you inject PersonalDetailsService:

[ApiController]
[Route("api/ApplicantDetail")]
public class ApplicantController
{
    private readonly PersonalDetailsService _personalDetailsService;

    public ApplicantController(PersonalDetailsService personalDetailsService)
    {
        _personalDetailsService = personalDetailsService;
    }

    [HttpPost("")]
    public async Task<IActionResult> Post([FromBody] Personaldetail ApplicantDetail)
    {
        ...

        var response = await _personalDetailsService.SomeMethodAsync(someParam);

        return null;
    }
}
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • why can't i call api of controllerA from another api Controller B directly ....? – Alpha977 Oct 22 '19 at 12:41
  • 7
    Because that's not how this works. Controllers aren't just classes. They depend on the request pipeline, and unless you "call" them via a request, things just don't work as they should. You can try to compensate in all sorts of hacky ways, but that just makes your code more brittle and harder to maintain. Besides, they would have to exist in the same project, and they may not always even if they do now. You don't want to lock yourself into a particular design. – Chris Pratt Oct 22 '19 at 12:45
  • 3
    Finally, even if they will live in the same project, the *correct* approach is to factor out the logic you need to share into some sort of helper class, which then both controllers can use, not call one controller action from another. – Chris Pratt Oct 22 '19 at 12:46
  • 2
    @ChrisPratt -- you explained that exceptionally well. I usually just heave a heavy when I get asked that question. – David Tansey Oct 22 '19 at 14:59
3

If this is within the same process it's unwise to call a controller from another controller.

It's more common to create a class, usually called a service, in which you put your logic to apply the task at hand.

This will have some benifits:

  • isolation of the logic
  • not having to worry about it when changing one endpoint
  • not worry about changing url's or authentication
  • not having an unnecessary network action

E.g.:

The service:

public class YourService
{
    public void YourMethod(parameters)
    {
       //do your stuff
    }
}

The usage:

public class Controller1 : Controller
{
    public void YourAction1()
    {
        //controller specific stuff like model validation      

        //shared logic
        var service = new YourService();
        service.YourMethod();
    }
}

public class Controller2 : Controller
{
    public void YourAction2()
    {
        //controller specific stuff like model validation      

        //shared logic
        var service = new YourService();
        service.YourMethod();
    }
}

Alternativly you can use a DI framework to resolve your service.

Stefan
  • 17,448
  • 11
  • 60
  • 79
0

I could see that your target method (PostPersonaldetail) on controller2 (Personaldetail Controller) is an asynchronous method. While we are calling it we need to use the await keyword with async applied to the method (POST: api/ApplicantDetail) as we can't use await keyword in a method with out making the method as async. Alternatively we can apply wait on the method call too.

Controllers are nothing but classes, we can create an instance and call methods defined in it, however it's not a good practice. Using dependency injection we can get references to other services/controllers with in the application and use it to call methods defined on them.

Approach 1:

 public async IActionResult Post([FromBody] Personaldetail ApplicantDetail)
 {
    var response = await new controller2().PostPersonaldetail(persondetails);
 }

Approach 2:

 public IActionResult Post([FromBody] Personaldetail ApplicantDetail)
 {
    var response = new controller2().PostPersonaldetail(persondetails).Wait();
 }

The following links can be helpful.

Resource 1

Resource 2

Sreekanth
  • 385
  • 1
  • 6
  • 17
  • thanks for you help. its not work for me but i figure it out your description helps me. – Alpha977 Oct 24 '19 at 07:14
  • I agree this is a brute way and we should never call from one controller to another.. I stumbled upon this question as some smart ass interviewer asked me how to call an action between controllers. – winterishere Oct 10 '20 at 21:23
-1

I don't think you want to new up an instance because it could be missing dependencies. You can do something like:

var controller = DependencyResolver.Current.GetService<PostPersonaldetail>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);

Then make the call.

You could also use dependency injection and just have a reference in the controller:

class ApplicantController : ControllerBase
{
    public ApplicantController(ControllerB controllerb)
    {
        _contollerB = controllerB;
    }

you may have to add a call to add the transient service:

services.AddTransient<ControllerB>();
JackSojourn
  • 324
  • 2
  • 15