What should be the return type of WEB API Action Method? - asp.net-core

I am developing ASP.NET Web API using .NET Core. This Web API is going to be mainly accessed by UI application (UI will be developed using ASP.NET Core MVC) but in future API may be accessed by other applications as well.
In my WEB API all methods are async.
If I want client to do content negotiation then what should be the return type of the API action method Task<IActionresult> or Task<SomePOCO>
If I want method to always return data in JSON format then what should be return type of the API action method? Should it be Task<IActionResult> or Task<JsonResult> or Task<SomePOCO> because I think all 3 would work so not sure which one is appropriate here?

If I want [the] client to do content negotiation then what should be the return type of the API action method?
To do content negotiation, return Task<ObjectResult> or Task<MyPoco>.
The framework automatically wraps POCOs in an ObjectResult; therefor, both options are equivalent, and both will obey the HTTP Accept header. You will also get content negotiation by returning any result that implements ObjectResult, such as an OkObjectResult does.
If I want [the] method to always return data in JSON format then what should be [the] return type of the API action method?
To always return JSON, return a Task<JsonResult> (or use the [Produces] filter).
See also: https://docs.asp.net/en/latest/mvc/models/formatting.html#content-negotiation
[S]o I am assuming then IActionResult is only used for MVC controller?
IActionResult is the contract for all results that a Controller returns. If your action's signature has an IActionResult return type, then your action's method body can return any result type, because all of them implement the IActionResult interface. Here is the inheritance hierarchy.
IActionResult
ActionResult
ChallengeResult
ContentResult
EmptyResult
FileResult
FileContentResult
FileStreamResult
PhysicalFileResult
VirtualFileResult
ForbidResult
LocalRedirectResult
ObjectResult
CreatedAtActionResult
CreatedAtRouteResult
CreatedResult
BadRequestObjectResult
NotFoundObjectResult
OkObjectResult
RedirectResult
RedirectToActionResult
RedirectToRouteResult
SignInResult
SignOutResult
StatusCodeResult
NoContentResult
NotFoundResult
OkResult
UnauthorizedResult
UnsupportedMediaTypeResult
BadRequestResult
See also: https://github.com/aspnet/Mvc/tree/dev/src/Microsoft.AspNetCore.Mvc.Core

Have a look at swagger:
https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger
It's better to specify ProducesResponseType attribute for method:
[ProducesResponseType(typeof(TodoItem), 201)]
public IActionResult Create([FromBody, Required] TodoItem item)
So that automatically generated swagger documentation to show the actual returned data for the method.

Related

How to handle FileResponse return type of Nswag methods

I am using Nswag to generate client library to consume an API created from .NET Core Application.
Currently I am using actions that returns IActionResult to benefit from methods such as return NotFound(); and return Ok(objectValue);
If the return type is IActionresult generated Csharp Client methods from Nswag have return type of FileResponse.
If I change return type to a certain class then the same return type is returned from the generated methods. This will prevent to use the benefits that I mentioned before.
Usually the return type is from a known return type and never return an anonymous type.
I have managed to get the response from reading the Stream from FileResponse and converted it to an object with a generic method JsonConvert.DeserializeObject<T>(value); but this seems to be extra work needed to be done.
Is there anything I am missing to handle FileResponse return type? Is it better to change from IActionResult to a known object?
I managed to solve the problem by changing actions to return ActionResult<ObjectType> instead of IActionResult. For more detail look at this answer.
NSwag client generator requires additional annotations to generate accurate client code. FileResponse is returned for IActionResult - as you stated. If action returns more codes then ProducesResponseType helps.
Example using IActionResult (string is returned):
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpPost("foo")]
public IActionResult FooAction([FromBody] string param)
Example using ActionResult<>:
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpPost("foo")]
public ActionResult<string> FooAction([FromBody] string param)
Need to know that ASP .NET Core uses ProblemDetails (it's configurable) for error codes generated using build-in parameterless methods. If those methods are called with parameter, then ProducesResponseType will require to add type - without it wrong code will be generated.
You can do one of the following:
Change the return type (e.g.: Instead of IActionResult use FileStreamResult)
Use the below code to get the file in SwaggerUI:
public async Task<Stream> GetTemplateAttachmentAsync(string fileName)
{
//code here to Get the file from AzureBlob or storage
}
// calling method
var content = GetAsync(fileName, cancellationToken);
return File(content.Stream, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);

Is it possible to modify Web Api Parameter (FromUri) inside Action Filter?

My Web API receives a param object consisting of (DateTime) dateFrom, (DateTime) dateTo.
Currently I preprocess the parameter inside my API entry function, which, is repetitive throughout the API.
I wish I could access and modify the parameter before it enters the API entry function.
So I have the action filter below:
public class MyActionParamFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// pre-processing
object param;
object param2;
actionContext.Request.Properties.TryGetValue("dateFrom", out param);
actionContext.ActionArguments.TryGetValue("dateFrom", out param2);
}
}
From the image, it is clearly that i have passed the parameters to the API. But I have no idea why param and param2 are null...
Is it possible to achieve that?
Finally I follow the answer here:
Change webapi controller action parameter in delegatinghandler
Technically, it is to modify actionContext.ActionArguments.
But I am not too sure if this is 'Best practice' or something.. Let me know your thought if you think i am in the wrong direction of design.

Which filter to manually deserialize incoming JSON to a specified type

I'm developing an application with ASP.NET Core WebAPI. What I would like to do is create a filter that I can apply to my controller method that will then let the system know to explicitly convert the incoming JSON object into a specified type.
For example, this is what I envision being able to do:
[HttpPost()]
[MyFilter(typeof(MyType))]
public IActionResult Post(MyType model)
{
....
}
The reason I want to do this is because the incoming JSON object does not match (at all) the structure of "MyType". So I have written a special converter whose logic I can call from this Filter. I want to explicitly state the type and not try to infer it from values within the JSON object (which is, in my case, impossible).
What I would like to do is create a filter that I can apply to my
controller method that will then let the system know to explicitly
convert the incoming JSON object into a specified type.
Filter is not quite right tool for performing custom model binding you described. You should not reinvent the wheels here: custom model binding should be performed by means of (sorry for this pun) custom model binder.
If the only argument against model binder is:
I cannot use CustomModelBinder as that is injected into the pipeline.
then ASP.NET Core provides an elegant way how to apply model binder to specific action arguments, without adding it to the whole pipeline. This could be done via ModelBinder attribute applied to action paramter, which specifies the binder in BinderType property.
Here is a sample:
[HttpPost]
public IActionResult Post([ModelBinder(BinderType = typeof(MyTypeBinder))] MyType model)
{
return Ok();
}
public class MyTypeBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
// Put your custom MyType binding code here
}
}

What's causing the parameter to be null?

I am using Asp.Net Core 2.0 and web api to build a rest service. All works fine except for HTTPPost.
[HttpPost("LoginUser")]
public IActionResult LoginUser(LoginUser loginUser)
{
return Ok(loginUser);
}
loginUser is always null. I am testing with fiddler and my route is http://localhost:53250/api/User/LoginUser
and the body is
{"EmailAddress":"xx#xx.com","Password":"123456789"}
Fiddler hits the link just fine, but payload is always null.
I have also tried
[HttpPost("LoginUser")]
public IActionResult LoginUser([FromBody] LoginUser loginUser)
{
return Ok(loginUser);
}
In this case, it doesn't hit the function.
This is the LoginUser definition:
public class LoginUser
{
public string EmailAddress { get; set; }
public string Password { get; set; }
}
Any Ideas?
Your action should be:
[Route("api/[controller]")]
public class UserController : Controller
{
[HttpPost("LoginUser")]
public IActionResult LoginUser([FromBody] LoginUser loginUser)
{
return Ok(loginUser);
}
}
See, [HttpPost("LoginUser")] affects only route and doesn't relate to LoginUser object type.
Update: you need [FromBody] as ASP.NET Core model binding by default looks into [FromForm] binding source. And [FromBody] attribute indicates that you want to bind a parameter to data in the request body.
Update 2: you also should add Content-Type: application/json header to request. ASP.NET Core selects input formatters based on the this header.
Update 3: if you really need to get body data as raw string, look into ASP.NET Core MVC : How to get raw JSON bound to a string without a type?. It suggests using [FromBody] dynamic data
JSON parsing is case sensitive. Your JSON is in the wrong case.
Should be: {"EmailAddress":"xx#xx.com","Password":"123456789"}.
Issue has been solved. When I added my UserController, I did so as a class and derived from controller. I deleted it and added it as a new item and picked web api core controller. Now all is working just fine. Thanks for your help.
If you have properties in your request model that are set to {get;
private set;}, the values will not get populated. Make them public by removing private. Also constructors
aren't utilized.
If you're reading plain text from the body, see if [FromForm]
works.

How to have 2 controller methods with same name in Asp.net MVC4

Is it possible to have 2 controller methods with same name ?
You're referring to controller methods which confuses me what you're talking about. Is it C# class methods or controller actions? Let me answer to both.
Pure C# doesn't allow that
It's not possible to have two methods with identical signature in C#. This means same name and same number of parameters with same types.
public int Calculate(int a, int b) { ... }
public int Calculate(int first, int second) { ... } // compiler ERROR
But Asp.net MVC controller actions allow it
If you're talking about Asp.net MVC controller actions, that's of course possible. Use ActionName attribute to accomplish what you require:
public ActionResult Common() { ... }
[ActionName("Common")]
public ActionResult CommonAgain() { ... } // C# signature differs
But there will have to be some other method selector attribute on one of these for the action invoker to know which one to use when request comes in... As they are you'd have a 404 runtime error.
It may be that one should be a regular request action but the other should be executed when Ajax request comes in. Or similar. Some distinction is required.