On startup, how do I execute an async function inside another class by redirecting a route?
The code below first runs the Home() function inside the StartupController() class but I want to run the GetString() function in the HelloApiController() class and pass the string variable. The method I am using doesn't work, is there something I am missing?
public class StartupController()
{
[HttpPost]
[Route("api/forge/run")]
public async Task<dynamic> Home()
{
string text = "Hello Api";
RedirectToRoute("api/forge/String", "text");
return null;
}
}
public class HelloApiController()
{
[HttpGet]
[Route("api/forge/String")]
public async Task<dynamic> GetString(string text)
{
return text;
}
}
Related
This is my code.
I'm trying to overload GET with 2 function :
With one parameter
With two parameter
I'm getting Swagger error "Failed to load API definition". Why ?
[Route("api/[controller]")]
[ApiController]
public class HospitalizedController : ControllerBase
{
[HttpGet("")]
public string Get(string MedicID)
{
string jsonData;
string connString = gsUtils.GetDbConnectionString();
// dosomething
}
[HttpGet("")]
public string Get(string MedicID, string PatientID)
{
string jsonData;
string connString = gsUtils.GetDbConnectionString();
//do something
}
}
The error "Failed to load API definition" occurs because the two methods are on the same Route.
You can specify a more specific route to distinguish them, like this:
[Route("api/[controller]")]
[ApiController]
public class HospitalizedController : ControllerBase
{
[HttpGet]
[Route("{medicId}")]
public string Get(string medicID)
{
}
[HttpGet]
[Route("{medicId}/patients/{patientId}")]
public string Get(string medicID, string patientID)
{
}
}
I'm curious is it possible to execute function Function1 and function Function2 in C# webapi project like in example below.
Both of these functions are on the same class and use async and await but return different types.
Example code below:
[ApiController]
[Route("[controller]")]
public class ClassController : ControllerBase
{
// ...
public async Task<ActionResult<string>> Test()
{
var message = await _myClass.Function1().Function2();
return Ok(message);
}
// ...
}
Declaration of _myClass looks like below:
public class MyClass
{
// ...
public async Task<MyClass> Function1()
{
// code which uses `await` below
// ....
// end of this code
return this;
}
public async Task<string> Function2()
{
// code which uses `await` below
// ....
// end of this code
return "some text";
}
}
Yes, you can do this:
public async Task<ActionResult<string>> Test()
{
var message = await
_myClass.Function1().ContinueWith(resultingMyClass =>
resultingMyClass.Result.Function2());
return Ok(message);
}
But the most obvious solution would be to use the async/await syntax that C# provides:
public async Task<ActionResult<string>> Test()
{
var result1 = await _myClass.Function1();
var message = await result1.Function2();
return Ok(message);
}
If you don't need the result from the first call as input to the second function you can run multiple Tasks in parallel and wait until they have all finished using Task.WhenAll.
I really Didn't get proper Title for this question. Please correct it if its misleading.
I have a WebApi controller, where there are multiple validation check are there. Controller sample code
public async Task<IActionResult> UploadFile(IFormFile file)
{
try
{
return file.IsValid();
//some more Functionality
}
}
Here Isvalid is a Extension method where the code is as follows
public static IActionResult PrepareResult(this ControllerBase controller, IFormFile file)
{
if (file== null)
{
return controller.Badrequest("No data sent");
}
return controller.Ok();
}
Issue:- In current scenario , if the file is Null then Extension method will be returning Badrequest() & the same will be returned to the client. But if file is not null then It's going to return Ok() & same will be returned to the Clint, where as i have more code to execute(i.e.//some more Functionality).
I don't want to return controller.Ok(), so that for positive scenario i can continue with my remaining code.
NB:- i don't want to assign to any variable & check with If condition. In order to avoid if condition only i am using extension methods.
Not sure why you don't want to assign varaibles and avoid if condition as this is the most efficient way. You can use exception handling, though that comes with a performance cost.
public static void EnsureFileIsValid(this IFormFile file)
{
if(file == null) { throw new InvalidOperationException("No data sent"); }
}
public async Task<IActionResult> UploadFile(IFormFile file)
{
try
{
file.EnsureFileIsValid();
return Ok();
}
catch(InvalidOperationException ex)
{
return BadRequest(ex.Message);
}
}
You can pass a action to your method like :
public static IActionResult PrepareResult(this ControllerBase controller, IFormFile file, Action<IFormFile> work)
{
if (file == null)
{
return controller.Badrequest("No data sent");
}
work(file);
return controller.Ok();
}
In you action, the use is :
public class FilesController : ControllerBase
{
[HttpPost]
public IActionResult UploadFile([FromServices]IFileService fileService, IFormFile file)
{
return Ok(PrepareResult(file, fileService.Upload));
}
}
But maybe you can consider to use validation.
In validation step, you enforce a parameter isn't null with RequiredAttribute.
ApiControllerAttribute enforce the validation before the action is called. If the validation fail, then ASP.NET Core return directly BadRequest and the action isn't called.
In this example, if the parameter file is null then the action isn't called and it return BadRequest :
[ApiController]
public class FilesController : ControllerBase
{
[HttpPost]
public IActionResult UploadFile([Required]IFormFile file)
{
//Do something
return Ok();
}
[HttpPut]
public IActionResult UploadFileBis([Required] IFormFile file)
{
//Do something
return Ok();
}
}
PS : You can use [ApiControllerAttribute] at assembly level, it will be enable to all controllers in assembly :
[assembly: ApiController]
If you want to verify conditions and define the error response in the same lower layer class, here is a solution.
First, let's create a custom exception that will hold the http status code and message to return:
// serialization implementation redacted
public class InfrastructureException : Exception
{
public HttpStatusCode HttpStatusCode { get; }
public InfrastructureException(HttpStatusCode code, string message) : base(message)
{
HttpStatusCode = code;
}
}
We need a class to handle the response serialization:
public class ExceptionResponse
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Then create a middleware that handle exceptions:
public class InfrastructureExceptionMiddleware
{
private readonly RequestDelegate next;
public InfrastructureExceptionMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext httpContext, IHostEnvironment hostEnvironment)
{
try
{
await this.next(httpContext);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
ExceptionResponse response = exception is InfrastructureException infrastructureException
? new ExceptionResponse()
{
StatusCode = (int)infrastructureException.HttpStatusCode,
Message = infrastructureException.Message
}
: new ExceptionResponse()
{
StatusCode = (int)HttpStatusCode.InternalServerError,
Message = ReasonPhrases.GetReasonPhrase(context.Response.StatusCode)
};
return context.Response.WriteAsync(response.ToString());
}
}
Now, we need to register our middleware:
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// redacted
app.UseMiddleware<InfrastructureExceptionMiddleware>();
// redacted
}
}
In the controller, we delegate the validation to the extension method:
public async Task<IActionResult> UploadFile(IFormFile file)
{
file.IsValid();
// now you can consider file is valid
}
Finally, in the extension method, we throw the exception:
public static void IsValid(this IFormFile file)
{
if(file == null)
{
throw new InfrastructureException(HttpStatusCode.BadRequest, "No data sent");
}
if(...) // condition for Http NotFound
{
throw new InfrastructureException(HttpStatusCode.NotFound, "Data not found");
}
// other validation conditions
}
{
public class MyClass
{
// all the call to GetData() of apiHelper should pass through this method
public async Task<T> InitiateAPICallAsync<T>(Task<T> apiCall) where T : BaseResponse
{
var response = await apiCall;
// some common code work using response data
return response;
}
public async void MyFunc()
{
var helper = new APIHelper("1", "2");
//
var response1 = await InitiateAPICallAsync(helper.GetData<Response1>()); // correct way
var rewponse2 = await helper.GetData<Response1>(); // incorrect way, need to show warning
}
}
public class APIHelper
{
public APIHelper(string a, string b)
{
// some code
}
public async Task<T> GetData<T>()
{
await Task.Delay(1000); // network call
// other code
return default;
}
}
public class Response1 : BaseResponse { }
public class Response2 : BaseResponse { }
public class BaseResponse { }
}
in my application MyClass, there is a method named InitiateAPICallAsync(). All call to the GetData() method of APIHelper must be pass through this method. I need to showing warning, if GetAsync() method called directly without passing through InitiateAPICallAsync.
Note: It is a sample code snippet, where in my real time project the APIHelper represents a Connectivity library. and MyClass represents another library named service.
How to show warning for a method if it is called directly in c#
Using CallerMemberName attribute is core thread of the following solution, thanks for Fumeaux's comment, I tried place CallerMemberName attribute above GetData method directly to get the caller, but the result is MyFunc but not InitiateAPICallAsync. So I tried use delegate as the InitiateAPICallAsync parameter that could make sure GetData will called by InitiateAPICallAsync. The following code has been simplified.
public delegate Task<int> PrintCaller([CallerMemberName] string Caller = null);
public class MyClass
{
public async Task<string> InitiateAPICallAsync(PrintCaller apiCall)
{
var response = await apiCall();
return "Test";
}
public async void MyFunc()
{
var helper = new APIHelper();
var str1 = await InitiateAPICallAsync(new PrintCaller(helper.GetData));
var str2 = await helper.GetData();
}
}
public class APIHelper
{
public async Task<int> GetData([CallerMemberName] string Caller = null)
{
if (Caller == "InitiateAPICallAsync")
{
// do some thing
}
else
{
//Show Warning
var dialog = new MessageDialog("Waring!!! Please don't call it directly");
await dialog.ShowAsync();
}
return 0;
}
}
I am trying to receive the below key value pair as the input parameter to my Web API
json=%7B%0A%22MouseSampleBarcode%22%20%3A%20%22MOS81%22%0A%7D%0A
where the right of the string is the URL encoded JSON which looks like
{
"MouseSampleBarcode" : "MOS81"
}
How can I parse this and store them in to the Model class
[HttpPost]
public async Task<IHttpActionResult> Get([FromBody] CoreBarCodeDTO.RootObject coreBarCode)
{
string Bar_Code = coreBarCode.MouseSampleBarcode.ToString();
where the CoreBarCodeDTO looks like below
public class CoreBarCodeDTO
{
public class RootObject
{
public string MouseSampleBarcode { get; set; }
}
}
You could do it this way. Change your class to this definition. In your controller coreBarCode.json will have the the json which you can then work with as needed:
public class CoreBarCodeDTO
{
private string _json;
public string json { get { return _json; }
set {
string decoded = HttpUtility.UrlDecode(value);
_json = decoded;
}
}
}
Update
[HttpPost]
public async Task<IHttpActionResult> Get([FromBody] CoreBarCodeDTOcoreBarCode coreBarCode)
{
string Bar_Code = coreBarCode.json;
//work with the JSON here, with Newtonsoft for example
var obj = JObject.Parse(Bar_Code);
// obj["MouseSampleBarcode"] now = "MOS81"
}
As #Lokki mentioned in his comment. The GET verb does not have a body, you need to change that to POST or PUT (depending if you are creating/searching or updating), so your code would look like this:
[HttpPost("/")]
public async Task<IHttpActionResult> Get([FromBody] CoreBarCodeDTO.RootObject coreBarCode)
{
string Bar_Code = coreBarCode.MouseSampleBarcode.ToString();
So, as I said: Get doesn't have body.
Follow #KinSlayerUY answer.
[HttpPost("/")]
public async Task<IHttpActionResult> Post([FromBody] CoreBarCodeDTO.RootObject coreBarCode)
{
string Bar_Code = coreBarCode.MouseSampleBarcode.ToString();
...
}
If you need use GET remove [FromBody] attribute and send data as single parameters
[HttpGet("/")]
public async Task<IHttpActionResult> Get(string mouseSampleBarcode)
{
var rootObject = new CoreBarCodeDTO.RootObject
{
MouseSampleBarcode = mouseSampleBarcode
}
...
}