I'm new to the .NET framework and my company doesn't use Core yet, so I'm trying to figure out why my web application api is showing a 400. I had a normal web forms project and added a controller class named TagController.cs. My project is on port 44318 and I've tried accessing localhost/44318/api/tag with no luck. I also tried adding a controllers folder with api sub folder and the controller inside it, but to no avail. I've posted images of my project hierarchy and the errors themselves. I have a feeling that the project not having a global.asax could have something to do with it, but there is one in another project. Maybe TagController.cs is pointing to another port? Any help is greatly appreciated.
TagController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using ClarityDBWebFormsRedis;
using StackExchange.Redis;
namespace ClarityDBWebFormsRedis
{
public class TagController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(string data) {
return "doge";
}
// POST api/<controller>
public void Post([FromBody] string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
You need a default (route) configuration in the project, so that it knows what it should do with the ApiControllers, or how the API can be called. This is defined for example in the Global.asax. You can simply put your class TagController into a folder called "Controllers".
The Global.asax looks then accordingly e.g. like this:
using System.Web.Http;
using System.Web.Routing;
(...)
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional });
}
An ApiController for example looks like this:
public class PingController : ApiController
{
[HttpGet, AllowAnonymous]
public IHttpActionResult Get()
{
return Ok();
}
}
For normal pages it is enough to create an .Aspx page and then call it in the browser according to the created folder structure. If you use MVC, then this page is created in different files and folders in the project (Views/Home.cshtml, Models/HomeViewModel.cs and Controllers/HomeController.cs).
Related
I'm trying to using ASP.NET
I'd like to put slashes in url for various addresses on MVC Controller.
This is url that I'm thinking of:
localhost/file/a/d/aaa.zip?key=randomkeystring
first a is drive name, d is folder's name, aaa.zip is file name, key query is own key to allow requests.
Just wanted to know how can I allow / on Controllers.
Here is some codes I made it (I think this is not related or so)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Server.Controllers
{
[Route("file/")]
[ApiController]
public class FileController : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetFile(string uri)
{
//if (!Directory.Exists(uri.Substring(0, uri.IndexOf("/"))))
//return File(System.IO.File.OpenRead("");
return Ok(uri.Substring(0, uri.IndexOf("/")));
}
}
}
Use [Route("file")] over controller.
Add [HttpGet("{driveName}/{folderName}/{fileName}")] above action. Also update action parameters as GetFile(string driveName, string folderName, string fileName).
It indicates that for localhost/file/a/d/aaa.zip?key=randomkeystring after file/ it will map a to driveName route parameter, d to folderName route parameter and so on.
Check this for more details about Route https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-3.1
namespace Server.Controllers
{
[Route("file")]
[ApiController]
public class FileController : ControllerBase
{
[HttpGet("{driveName}/{folderName}/{fileName}")]
public async Task<IActionResult> GetFile(string driveName, string folderName, string fileName)
{
//if (!Directory.Exists(uri.Substring(0, uri.IndexOf("/"))))
//return File(System.IO.File.OpenRead("");
return Ok(uri.Substring(0, uri.IndexOf("/")));
}
}
}
I am learning how to create React applications with ASP.NET Core. As a newbie I am starting at the very beginning and trying to get "Hello World" displayed on the home page. I have used Visual Studio's default React.js project template to get me started. The routes are set to default. Here are my files:
Home.js:
import React, { Component } from 'react';
export class Home extends Component {
constructor(props) {
super(props);
this.state = { message: "" };
fetch('api/Home/Message')
.then(response => response.json())
.then(data => {
this.setState({ message: data });
});
}
render () {
return (
<h1>{this.state.message}</h1>
);
}
}
HomeController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace TestingReactDotNet.Controllers
{
[Route("api/[controller]")]
public class HomeController : Controller
{
[HttpGet]
public async Task<IActionResult> Message()
{
var response = "Hello World";
return Ok(response);
}
}
}
The problem is that the HTTP response that is being parsed to Json isn't the correct one. I have console.logged it out in order to try to debug, and it appears that response.json()) is retrieving all of the text in the default public/index.html file that comes with the template application. Does anyone have any idea why this is?
Apologies if I am missing something super obvious - I use a Mac, so the file structure and Visual Studio IDE are quite different and I have struggled to understand quite a few of the tutorials/answers already out there.
To hit your Message() function you must make a HttpGet to 'api/Home' not 'api/Home/Message'.
If you want your endpoint to be 'api/Home/Message' then you must specify the route for the Message() function like so:
// api/Home/Message
[HttpGet, Route("Message")]
public async Task<IActionResult> Message()
Class Controller is for MVC which does generate full web pages.
For Web API you need to extend ControllerBase. And you should just return your value/object straight:
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet, Route("message")]
public async Task<string> Message()
{
var response = "Hello World";
return response;
}
}
I solved the problem by adding a context line in setupProxy.js
You can locate it at
ClientApp -> src -> setupProxy.js
You must add the name of controller you want to use in the array context list.
I am trying to add WebAPI endpoints to an existing forms application but I am getting 404 errors.
I cannot use the Global.asax Application_Start() to register the api routes as suggested by Microsoft here because the current application already has a compiled customization of the Global class which inherits from HttpApplication and they did not mark any of the methods as virtual. doh!
I am trying to load the routes using an HttpModule. I am getting 404 errors for the following URL:
https://example.com/webapplication/myapi/Authorize/User
Module code:
using System;
using System.Web;
using System.Web.Http;
public class MyHttpModule : IHttpModule
{
private static bool HasAppStarted = false;
private readonly static object _syncObject = new object();
public void Init(HttpApplication context)
{
//https://stackoverflow.com/a/2416546/579148
if (!HasAppStarted)
{
lock (_syncObject)
{
if (!HasAppStarted)
{
GlobalConfiguration.Configure(config => RegisterRoutes.Load(config));
HasAppStarted = true;
}
}
}
}
#region IDisposable Implementation
#endregion
}
My registration class is in a standalone library:
using System.Web.Http;
public static class RegisterRoutes
{
public static void Load(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute("DefaultApi", "myapi/{controller}");
}
}
And my controller:
using System;
using System.Web.Http;
using MyLibrary.Models;
[Route("myapi/[controller]/[action]")]
public class AuthorizeController : ApiController
{
[HttpGet, ActionName("User")]
[AllowAnonymous]
public WebUser GetUser()
{
WebUser user = new WebUser();
user.Key = Guid.Empty.ToString();
return user;
}
}
And finally the web.config:
<configuration>
<!--...-->
<system.webServer>
<!--...-->
<modules>
<add name="MyModule" type="MyLibrary.MyHttpModule" />
</modules>
<!--...-->
</system.webServer>
<!--...-->
<configuration>
The webapplication is its own IIS application (its own Global.asax and web.config). I am on Windows 10, CLR version v4.0, and Managed Pipeline is Integrated.
I've tried several other options described here, here, and here but have not had anything but 404s.
TIA!
You are mixing up frameworks. [Route("myapi/[controller]/[action]")] is for asp.net-core while your code appears to be for asp.net-web-api
A few suggested changes.
Module code can be simplified
GlobalConfiguration.Configure(RegisterRoutes.Load);
Since attribute routing is configured
config.MapHttpAttributeRoutes();
Use the correct attributes on the APIController and action
[RoutePrefix("myapi/Authorize")]
public class AuthorizeController : ApiController {
[HttpGet, ActionName("User")]
[Route("user")] //GET myapi/authorize/user
[AllowAnonymous]
public IHttpActionResult GetUser() {
WebUser user = new WebUser();
user.Key = Guid.Empty.ToString();
return Ok(user);
}
}
Turns out the problem was in the MapHttpRoute() method call. It seems that the routing does not like not having a value for the defaults and constraints parameters. I updated the map call to this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: Constants.ApiBaseRoute + "/{controller}/{action}",
defaults: new { },
constraints: new { }
);
I also had to add the action template parameter. And I removed the route attributes on the controller.
I am using the John Papa Single Page Application source code to create my own App and I am running into some problems when using the Breeze Web API. I have my own breeze controller and as soon as I add a second HttpGET method I get the error "Multiple actions were found that match the request".
It is Weird because in his code he adds multiple GETs and his code works but I think I am missing something.
Breeze Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Breeze.WebApi;
using AgencyUpdate.Models;
namespace AgencyUpdate.Controllers
{
[BreezeController]
public class BreezeController : ApiController
{
readonly EFContextProvider<AgencyDbContext> _ContextProvider =
new EFContextProvider<AgencyDbContext>();
public string MetaData()
{
return _ContextProvider.Metadata();
}
[HttpGet]
public IQueryable<api_Agency> GetAgency()
{
return _ContextProvider.Context.api_Agency;
}
[HttpGet]
public IQueryable<api_AgencyOffice> GetOffice()
{
return _ContextProvider.Context.api_AgencyOffice;
}
}
}
I use this URL to request data:
**http://localhost:13762/api/breeze/GetAgency**
Also I have found this .CS file for routing but I don't know if I have to make changes to it.
BreezeWebApiConfig
using System.Web.Http;
[assembly: WebActivator.PreApplicationStartMethod(
typeof(AgencyUpdate.App_Start.BreezeWebApiConfig), "RegisterBreezePreStart")]
namespace AgencyUpdate.App_Start {
///<summary>
/// Inserts the Breeze Web API controller route at the front of all Web API routes
///</summary>
///<remarks>
/// This class is discovered and run during startup; see
/// http://blogs.msdn.com/b/davidebb/archive/2010/10/11/light-up-your-nupacks-with-startup-code-and-webactivator.aspx
///</remarks>
public static class BreezeWebApiConfig {
public static void RegisterBreezePreStart() {
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{controller}/{action}"
);
}
}
}
Does anyone know what the problem is?
I feel a bit stupid the URL I need to use is breeze/breeze/MethodName.
John's code doesn't use breeze twice in the URL hence the confusion
Papa's course has the single-page-apps-jumpstart.zip file with project source code by chapters. The right version the BreezeWebApiConfig.cs content is as such:
public static class BreezeWebApiConfig {
public static void RegisterBreezePreStart() {
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "api/{controller}/{action}"
);
}
}
}
Notice the string routeTemplate: "api/{controller}/{action}"
I created a new ASP.NET MVC4 Web Api Project. In addition to the default ValuesController, I added another controller, ScenarioController. It has the exact same methods as ValuesController. But for some reason, it behaves differently.
/api/values/ => "value1","value2"
/api/values/1 => "value"
/api/scenario/ => "value1","value2"
/api/scenario/1 => "value1","value2"
^^^^^^^^^^^^^^^^^
should return "value"!
Using breakpoints, I know that /api/scenario/1 actually gets sent to the public IEnumerable<string> Get(), not the expected public string Get(int id). Why?
For reference, here are the relevant files (these are pristine default mvc4-webapi classes, haven't modified anything):
Global.asax.cs
namespace RoutingTest
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
WebApiConfig.cs
namespace RoutingTest
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
config.EnableSystemDiagnosticsTracing();
}
}
}
ValuesController.cs
namespace RoutingTest.Controllers
{
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
}
ScenarioController.cs (yes, it's in the Controllers folder)
namespace RoutingTest.Controllers
{
public class ScenarioController : ApiController
{
// GET api/scenario
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/scenario/5
public string Get(int id)
{
return "value";
}
}
}
Gremlins. Thanks to #Pete Klien for verifying that the code does work outside my machine. Here's what I did.
Experienced problem of Controller only using 1 method for Get in original project.
Created new Web Api project, with code that I posted in the question. Same symptom.
Clean Project, Rebuild All, still no dice.
Reboot machine, clean, rebuild, try again, no dice.
Create new Web Api project in new solution, success!
I tried your code just now and got the expected result:
> curl http://localhost:53803/api/values
["value1","value2"]
> curl http://localhost:53803/api/values/1
"value"
> curl http://localhost:53803/api/scenario
["value1","value2"]
> curl http://localhost:53803/api/scenario/1
"value"
>
(By the way, there is no requirement that it be in the Controllers folder. HttpConfiguration.Routes.MapHttpRoute simply finds all your classes that inherit from ApiController.)
I am not being sarcastic when I suggest that you Rebuild All and try again.
I was having this issue and could not get anything to work. Finally I changed the port on the IIS Express Project Url setting and all is back to normal. It was localhost:57846. I just made it localhost:57847 and all is back to normal.