MapHttpRoute with custom HttpMessageHandler - c#

So I'm having a troubles getting the route to work properly after the messagehandler has finished.
Error that shows up are:
No HTTP resource was found that matches the request URI
this is what I got so far:
http://localhost:51077/api/v1/project/getprojects?apikey=123456
// all actions under /project routes require authentication
config.Routes.MapHttpRoute(
"ProjectApi",
"api/v1/{controller}/{action}/{apikey}",
new { apikey = RouteParameter.Optional },
null,
HttpClientFactory.CreatePipeline(
new HttpControllerDispatcher(config),
new DelegatingHandler[]{new BasicAuthHandler(config)}));
// all routes requires an api key
config.MessageHandlers.Add(new ApiKeyHandler());
config.MapHttpAttributeRoutes();
[RoutePrefix("api/v1/ProjectController")]
public class ProjectController : BaseApiController
{
[HttpGet]
[Route("getprojects")]
public HttpResponseMessage GetProjects()
{
HttpResponseMessage resp = new HttpResponseMessage(HttpStatusCode.OK);
if(User.Identity.IsAuthenticated)
{
}
return resp;
}
}
So, all calls will first be checked if they have an ApiKey included to be able to connect (ApiKeyHandler) Then a popup appears and asks for username and password(BasicAuthHandler). If the log in is a success then it should be forwarded to the getprojects method under /project...
ApiKey is checked, username/password popup appears and is granted but then the error above comes and the route seems to be to invalid. I've tried different ways to get this to work but it seems I'm missing something here.

PROBLEM SOLVED
Just comment this line out and it works
[Route("getprojects")] // <--- COMMENT/REMOVE THIS LINE
Any ideas to why this was causing the problem?

Your issue doesn't replicate in my test that I tried at my end. It seems you have different names given to your controller in your RoutePrefix attribute. According to the request Url given:
http://localhost:51077/api/v1/project/getprojects?apikey=123456
RoutePrefix attibute on ProjectController class should look like:
[RoutePrefix("api/v1/Project")]

Related

Postman won't POST to ASP.Net WebAPI 2 controller, but GET works

I can't seem to get my webapi to work in PostMan and it gives me a 404 when POSTing, but works only when using GET (even though the api specifically set to accept only POSTs! - go figure!)
Here's the controller code (that works) - NOTE: I can't use formdata as this is dotnet fw 4.72
[Route("api/GetProfile")]
[HttpPost]
public async Task<IHttpActionResult> GetProfile(string UserId)
{
var retval = new Profile();
if (UserId != null)
{
if (await dbv.IsValidUserIdAsync(UserId))
{
retval = await profile_Data.GetProfileAsync(UserId);
}
}
return Ok(retval);
}
The code works fine for GET (even though it's set to accept POST!), which it shouldn't.
In PostMan, the URI is
https://localhost:44371/api/GetProfile
The route is 100% correct!
On the Body tab, it is set to RAW and the following JSON is inside
{"UserId" : "69d40311-f9e0-4499-82ea-959949fc34fe"}
The parameter is 100% correct!
The error when attempting to POST is
{
"Message": "No HTTP resource was found that matches the request URI 'https://localhost:44371/api/GetProfile'.",
"MessageDetail": "No action was found on the controller 'Accounts' that matches the request."
}
If I put the parameters in the querystring, it works (even though the controller is set to accept POST).
If I change the controller to GET and PostMan to GET (and set the parameters in params), it works.
Is PostMan not compatible with ASP.Net webapi 2.0 ?
Why would GET work and POST not work? Makes no sense?
Try to set Content-Type application/json on postman and in your controller's POST method add the attribute FromBody
[FromBody] isn't inferred for simple types such as string or int. Therefore, the [FromBody] attribute should be used for simple types when that functionality is needed.
[Route("api/GetProfile")]
[HttpPost]
public async Task<IHttpActionResult> GetProfile([FromBody] string UserId)
{
var retval = new Profile();
if (UserId != null)
{
if (await dbv.IsValidUserIdAsync(UserId))
{
retval = await profile_Data.GetProfileAsync(UserId);
}
}
return Ok(retval);
}
Also consider to return CreatedAtAction or CreatedAtRoute instead of Ok

ASP.NET Web Site Not Allowing Axios PUT Access (405 Method Not Allowed)

I've got a controller set up and working for all of my GET requests, but when it comes to the PUT requests my Web Site (not a Web App, if that makes any difference) is returning a 405.
I've got the route defined in my Global.asax Application_Start() with the other routes, and it's the first one, so it should be evaluated first (if my understanding is correct):
void Application_Start(object sender, EventArgs e)
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls
| System.Net.SecurityProtocolType.Tls11
| System.Net.SecurityProtocolType.Tls12;
RouteTable.Routes.MapHttpRoute("FilteredSpecialOrders",
routeTemplate: "api/sales/filteredRequests",
defaults: new { controller = "Sales", action = "filteredRequests" });
// subsequent routes are here
}
My SalesController has a method with the right attributes for type of request (Put) and the action name that matches my routeTemplate, as well as the [FromBody] attribute on the parameter:
[HttpPut, ActionName("filteredRequests")]
public IHttpActionResult PutSpecialOrders([FromBody] RequestFilter filter)
{
// do the needful
}
...and my client side code creates the body of the message (filter) as a JavaScript object and sends the .put requests via axios:
getFilteredRequests: async function () {
let filter = {
name: this.name,
age: this.yearsOld,
/* other name/value pairs of course */
};
const response = await axios.put(salesApi + 'filteredRequests', filter);
let data = response.data;
return data;
}
...but I'm always getting back a 405 - Method Not Allowed. What am I forgetting? I'm not sure how the JSON object that gets sent in the body is serialized into my RequestFilter object - does that happen automagically or do I need to define that somewhere? I've made sure that the names are the same on both ends, but other than that...
I edited my web.config to remove the WebDAV bits per this post - but I also had to switch my pipeline from classic mode to integrated to get it to work.

Error on try to post file from Angular 6 to ASP.NET Core Web Api

I'm trying to do a POST request with multipart/form-data from an Angular 6 application to a REST service in ASP.NET Core, but I have a Error 500. I tried too many solutions, but nothing worked. I tried to execute the request on Postman and get the same problem, but I tried in other software called ARC (Advanced REST Client) and works like a charm and I don't have any idea why.
On the server-side, I getting InvalidDataException: Missing content-type boundary. In my project, I'm using swagger too
here is my code
The request in angular:
public uploadPlanilha(planilha: File, idLote: number): Observable<Array<RequisicaoComposicao>>{
let formData = new FormData();
formData.append('arquivo', planilha, planilha.name);
formData.append('idLote', idLote.toString());
let httpHeaders = new HttpHeaders();
httpHeaders = httpHeaders.set("Content-Type", "multipart/form-data");
return this.httpClient.post<Array<RequisicaoComposicao>>(`${this.API_URL}requisicoes/upload`, formData, {
headers: httpHeaders
});
}
The controller method in Web Api
[HttpPost]
[Route("upload")]
[Consumes("multipart/form-data")]
[DisableRequestSizeLimit]
public ActionResult<List<RequisicaoComposicao>> PostPlanilhaRequisicoes([FromForm]ArquivoDto arquivoDto)
{
try
{
using (Stream arquivoPlanilha = arquivoDto.Arquivo.OpenReadStream())
{
List<RequisicaoComposicao> requisicaoComposicoes = _composicaoGestor.Inserir(arquivoPlanilha, int.Parse(arquivoDto.IdLote));
return Ok(requisicaoComposicoes);
}
}
catch (Exception)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
The ArquivoDto class
public class ArquivoDto
{
public IFormFile Arquivo { get; set; }
public string IdLote { get; set; }
}
Remove the [FromForm] tag you don't need it.
On this line here:
formData.append('arquivo', planilha, planilha.name);
change it to formData.append('arquivo', planilha);
I never used it, therefore I don't think you need the [Consumes("multipart/form-data")] attribute ether. (unless you are using something like swagger and you want to tell it what this method is consuming then keep it)
I'd also remove this line httpHeaders = httpHeaders.set("Accept", "multipart/form-data");
I use AngularJS + ASP.NET Core 2.1 and find the same error --> System.IO.InvalidDataException: Missing content-type boundary.
My solution is setting 'Content-Type: undefined' in http request front-end. I read somewhere at StackOverflow, you may give it a quick try if it helps.

Calling another Web API controller directly from inside another Web API controller

Given a controller Proxy and an action of GetInformation. I want to be able to call the method GetInformation of the Users controller. Both the WebAPI controllers are in the same project but direct calls like
var controller = new UsersController();
return controller.GetInformation(request);
Doesn't work.
The signature is:
public HttpResponseMessage GetInformation(InformationRequest request)
I do not want to do a full redirect response as I do not want the UserController route exposed externally. This needs to be an internal only call.
For those wanting to solve this API to API method in different controllers, we have found a solution that works. The initial attempt was close, just missing a few things.
var controller = new UserAccountController
{
Request = new HttpRequestMessage(HttpMethod.Post, Request.RequestUri.AbsoluteUri.Replace("/current/route", "/route/to_call"))
};
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration();
return controller.GetInformation(request);
In doing this it allows construction of the target controller and direct invocation of the method desired. The biggest complexity here is the Uri adjustment.
You should do something like this in your UsersController
public HttpResponseMessage GetInformation(InformationRequest request)
{
HttpResponseMessage resp ;
resp = UserBusinessLogic.GetInformation(request) ;
return resp ;
}
and from your ProxyController you can resuse that "UserBusinessLogic" method to obtain the same information using the same code snippet.
Another way can be:
IQueryable<Users> GetInformation()
without using the IHttpActionResult return type. Your method will still remain an Http GET method and then call it in the same way as you call any class method.

How to route Optional URI parameters to API Controller functions

I am trying to set up an Asp.Net forms site with an API.
I have succeeded in adding in selective authentication, so that pages starting "\api\" do not get redirected, but instead challenge for basic authentication.
I am now trying to use MS Web Api 2 to do the API routing.
The idea is to be as RESTful as possible. I have a resource, a TradableItem, and initially I would want to allow API users to use HTTP GET in one of two ways.
If the API user passes no item key, the user receives a list of possible item keys
["ABC","DEF"...]
If the API user passes an item key as part of the URI, eg "/api/tradables/abc", a representation of the TradableItem is returned for the one with the key=ABC. (To my understanding, this is standard REST behaviour).
In Global.ASAX's Application_Start() function I have a route map like so...
RouteTable.Routes.MapHttpRoute(
name: "TradableItemVerbs",
routeTemplate: "api/tradables/{item}",
defaults: new { item = System.Web.Http.RouteParameter.Optional, controller = "Tradable" });
The TradableController.cs file looks like this...
public class TradableController : ApiController
{
private static CustomLog logger = new CustomLog("TradableController");
// GET api/<controller>
public IEnumerable<string> GetKeys()
{
var prefix = "GetKeys() - ";
string msg = "";
msg = "Function called, returning list of tradable pkeys...";
logger.Debug(prefix + msg);
// Get a list of tradable items
return TradableManager.GetTradablePkeys();
}
// GET api/<controller>/<pkey>
public string GetTradable(string pkey)
{
string msg = string.Format("Would get Tradable data for key: >{0}<", pkey);
return msg;
}
}
The problem is that only the GetKeys() function fires, whether I call GET to "/api/tradables" or "/api/tradables/abc".
For reference, using VS2015 Community, IIS 7.5, targeting .Net 4.6.1. I used Rick Strahl's blog as a guide on this (among other sources).
http://weblog.west-wind.com/posts/2012/Aug/21/An-Introduction-to-ASPNET-Web-API#HTTPVerbRouting
please, change the name of your param to item (because, this is the name define in the routes):
public string GetTradable(string item)
{
....
}
or when you call the method be explicit with the parameter name: /api/tradables?pkey=abc

Categories