Cannot return HTML from a Controller - c#

My code fails to return HTML from a Controller. The browser returns HTTP ERROR 500 - "This page does not work".
The Controller is written in .net core 3.1. Here is the code:
[ApiController]
[Route("")]
public class MyController : ControllerBase
{
[HttpGet]
public async Task<ActionResult<ContentResult>> GetInformation()
{
string s = #"<!DOCTYPE html><html><head><title>.....";
return base.Content(s, "text/html");
}
}
However, returning a plain string works: (using string instead of ContentResult. However, this string is not interpreted as HTML and is written directly to the browser)
[ApiController]
[Route("")]
public class MyController : ControllerBase
{
[HttpGet]
public async Task<ActionResult<string>> GetInformation()
{
string s = #"something";
return s;
}
}

public async Task<ActionResult<ContentResult>> GetInformation()
Because you declare that the action returns an ActionResult<ContentResult>, I expect that the ASP.NET Core serialisation process attempts to serialise the ContentResult you return as a JSON object. This is neither intended nor something the framework can manage.
Controller action return types in ASP.NET Core web API goes into the details, but a more typical method signature for your scenario would look like this:
public async Task<IActionResult> GetInformation()
You could also specify ContentResult, which implements IActionResult, if you'd prefer to do that:
public async Task<ContentResult> GetInformation()
IActionResult allows an action to return different responses in different situations, using e.g. BadRequest, Content, or Ok.

Related

How can I post a string to an ASP.NET Core action method from Angular?

How can I post a string to an ASP.NET Core action method from Angular?
Here is what I am trying:
client side:
this.http.post(
"http://foo/bar/baz",
"foo",
{ responseType: "text" }
).pipe(
map(res => res)
);
server side:
[Route("bar")]
[ApiController]
public class FooController : BaseApiController
{
[Authorize]
[HttpPost("baz")]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetStr(string str)
{
return GenerateResponseMessage(str);
}
}
Whenever I am getting to the return GenerateResponseMessage(str); line using a breakpoint, the str is null. While I am expecting the str to be equal the "foo". What am I missing here?
Help me, please. I always thought that it is very easy to post or get anything with the help of Angular and ASP.NET Core, but it seems that the evil is in details, because I can not figure out the issue for too long.
I need to post the string. I am aware of the get option, but my specific case requires posting the string.
[Produces("application/json")]
[Route("api/bar")]
[ApiController]
public class FooController : ControllerBase
{
[HttpPost("baz")]
public async Task<ActionResult> Login([FromBody]string str)
this way you can solve
FromBody should do the trick:
public async Task<IActionResult> GetStr([FromBody] string str)

Why is index method not called when action name is not specified

I have a simple ASP.NET Core API with the following controller:
[ApiController]
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
public IActionResult Index()
{
return Ok("index");
}
public IActionResult Get()
{
return Ok("get");
}
}
When I hit URL:
"http://localhost:6001/api/home"
With a GET request, I get the following error:
"The request matched multiple endpoints."
Normally, if I don't specify an action name, shouldn't it call the Index() method?
In ASP.NET Core API, action methods are matched by a combination of HTTP verb used as attribute on the method (which is HttpGet by default if you don't add any) and the route parameter used with it. In that sense both your action methods looks similar to the routing system. It sees two HttpGet method with no route parameter. That's why both method matches the request -
"http://localhost:6001/api/home"
making the routing system confused which to select. That's exactly what the error message is saying.
You need to make your non-default action method more specific, so that the routing mechanism can differentiate between the two. Something like -
[HttpGet("data")]
public IActionResult Get()
{
return Ok("get");
}
Now your Index method will be matched as default action method with the above URL.
Both of your endpoints are identical. Use routes to differentiate them. For example:
[ApiController]
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
[HttpGet]
public IActionResult Index()
{
return Ok("index");
}
[HttpGet, Route("data")]
public IActionResult Get()
{
return Ok("get");
}
}
This would open up:
http://localhost:6001/api/home/
and
http://localhost:6001/api/home/data
respectively using GET
You could also change the verb, for example use HttpGet for one and HttpPost for another.

Handling multiple IDs in Route

As far as I can understand, the conventional routing in .NET Core MVC is [controller]/[action]/{id?}
However, I have the following POST request I'm trying to catch which looks like this:
myDomain/MyController/MyAction/userID/anotherID/myInfo
I have tried the following, but it doesn't seem to be working:
public class MyController : Controller
{
[HTTPPost]
[Route("MyAction/{userID:minlength(2)}/{anotherID:int}/myInfo")]
public IActionResult MyAction([FromRoute] string userID, [FromRoute] int anotherID, [FromBody] string stuffIWant)
{
return Ok();
}
}
Obviously I'm not handling the routing correctly, but I'm not sure how I would get userID and anotherID from that route. I've published this action to my site, and tried to do a test post with the same URL, but didn't get a response.
Change to:
public class MyController : Controller
{
[HTTPPost]
[Route("MyAction/{userID:minlength(2)}/{anotherID:int}/myInfo")]
public IActionResult MyAction(string userID, int anotherID, [FromBody] string stuffIWant)
{
return Ok();
}
}

Webapi and normal methods in the same controller?

With the introduction of the Apicontroller attribute in asp.net core 2.1, I wonder how do I get the api and normal methods to work in the same controller.
[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> SaveOrder(SaveOrderModel model)
{
//...
}
public async Task<IActionResult> CustomerOrders()
{
if (!User.IsInRole("Customer"))
return Challenge();
var customer = await _workContext.CurrentCustomer();
var model = await orderModelFactory.PrepareCustomerOrderListModel();
return View(model);
}
}
I can call post method /api/order/saveorder but cannot run the https://example.com/order/customerorders.
It shows an exceptions: InvalidOperationException: Action
'.CustomerOrders ' does not have an attribute route. Action methods on
controllers annotated with ApiControllerAttribute must be attribute
routed.
If I remove [ApiController] and [Route("api/[controller]")] on the controller level and instead put on the method level, then it surely works. still don't know if there's any better hybrid solution for these methods as i want to use this new ApiController feature.
[Route("/api/controller/saveorder")]
public async Task<IActionResult> SaveOrder(SaveOrderModel model)
Any input is greatly appreciated.
You are saying, that you cannot call https://example.com/order/customerorders. In your [Route("api/[controller]")] you define, that all Methods inside this controller will be available at https://example.com/api/order/.
So to call your method, you need to call https://example.com/api/order/customerorders.
If you want to stay with https://example.com/order/customerorders, you need to put the [Route] attributes at your methods:
[ApiController]
public class OrderController : ControllerBase
{
[HttpPost("api/order")]
public async Task<IActionResult> SaveOrder(SaveOrderModel model)
{
...
}
[HttpGet("order/customerorders")]
public async Task<IActionResult> CustomerOrders()
{
if (!User.IsInRole("Customer"))
return Challenge();
var customer = await _workContext.CurrentCustomer();
var model = await orderModelFactory.PrepareCustomerOrderListModel();
return View(model);
}
}

ASP.Net REST API 404

I'm trying to implement a REST API in ASP.Net MVC. This is my code:
[Route("api")]
[ApiController]
public class ContactsController : ControllerBase
{
[HttpGet]
[Produces("application/xml")]
[Route("api/areas/{areaName}/contacts/{contactID}")]
public ActionResult<XmlDocument> Get(string areaName, string contactID)
{
return new XmlDocument();
}
}
However, when I surf to /api/areas/foo/contacts/bar, I encounter HTTP 404.
How can I fix my code?
Note that ActionResult<T> is part of asp.net-core and not the previous asp.net-mvc version. This means that you originally tagged the question incorrectly.
That said, the URL being called does not match the route template of the intended action and thus you will get a not found when trying to call the action.
Reference Routing to controller actions in ASP.NET Core
The provided route has [Route("api")] on the controller and [Route("api/areas/{areaName}/contacts/{contactID}")] on the action, which when combined would result in a URL that looks like
api/api/areas/foo/contacts/bar
I believe the intention was to have api only once so I would suggest remove one of them
Either from the action
[Route("api")]
[ApiController]
public class ContactsController : ControllerBase {
[HttpGet]
[Produces("application/xml")]
//GET api/areas/foo/contacts/bar
[Route("areas/{areaName}/contacts/{contactID}")]
public ActionResult<XmlDocument> Get(string areaName, string contactID) {
return new XmlDocument();
}
}
or removing the prefix on the controller
[ApiController]
public class ContactsController : ControllerBase {
[HttpGet]
[Produces("application/xml")]
//GET api/areas/foo/contacts/bar
[Route("api/areas/{areaName}/contacts/{contactID}")]
public ActionResult<XmlDocument> Get(string areaName, string contactID) {
return new XmlDocument();
}
}

Categories