I have a simple Azure Worker Role in which I want to host Web API. I would like to setup the Web API controllers so that I can access them same way as a jQuery post so that I don't have to create shell classes and my Web API can remain flexible for use with jQuery in the future.
Q: How can I post from a C# client to a self hosted Web API controller in a worker role, without using shell classes?
I've worked with Web API in the past where it's hosted in a regular ASP.NET project and I am able to access it via jQuery doing something like this:
//Web API Controller
public class TestController : ApiController
{
public void Post(string item, string action)
{
//...
}
}
//jQuery
$.post("/api/test/", { item: "myItem", action: "someAction" }, function (data)
{
//...
}
For my worker role project, I followed the instructions on ASP.NET for Host ASP.NET Web API 2 in an Azure Worker Role and set up WebAPI. I'd like to be able to access the controller the same way from a C# client. However, the documentation on ASP.NET for Calling a Web API from a .NET Client uses classes, rather than individual variables. The code samples end up looking something like below. Edit: I have been able to successfully use this method to communicate data to the worker role.
//Shell Object
public class ShellObject
{
public string item;
public string action
}
//Web API Controller
public class TestController : ApiController
{
public void Post(ShellObject shellObject)
{
//Unpack object, then do action
//...
}
}
//C# Client
ShellObject shellObject = new ShellObject() { item = "myItem", action = "someAction" }
var response = await client.PostAsync("api/test/", shellObject);
Because I don't want to create unnecessary shell classes in multiple places in my code, I'd like to do something like below. Doing it this way also keeps my Web API flexible so that it can be used from a web based jQuery project in the future (which in my case is a very possible scenario). However, these methods doesn't work; the server returns a "Method Not Allowed" or the post just hangs. Additionally, the posts don't seem to be showing up in Fiddler. Edit: Additionally, because this is intended to be a general purpose server, it's possible for the Worker Role and the calling C# client to be in different solution, so that they cannot share class definitions. Additionally, serializing to JSON before the post is fine, as long as there isn't a need for shell classes.
//Web API Controller
public class TestController : ApiController
{
public void Post(string item, string action)
{
//...
}
}
//Simple Post
using (var wb = new WebClient())
{
var data = new NameValueCollection();
data["item"] = "myItem";
data["action"] = "myAction";
var response = wb.UploadValues("http://workerroleaddress/api/test/", "POST", data);
}
Q: How can I post from a C# client to a self hosted Web API controller in a worker role, without using shell classes?
Could this solve your problem?
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://test.myapi.com");
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(new { action = "myAction", item = "myItem" });
client.PostAsync("api/test",
new StringContent(json, Encoding.UTF8, "application/json")).Wait();
}
You could also use other serialization methods mentioned here.
Related
We have been developing a .NET MVC application and recently are looking to integrate a webhook for an email service. I'll admit, this is my first attempt at webhooks and Web API, but it looks fairly straight forward. I've followed several of the best practice and code examples from SendGrid and keep getting the "No type was found that matches the controller named 'xxxxxx'" message. I'm testing locally with Postman and can not get the controller(s) to be found. My initial goal is to test with the most basic configuration and just pass a POST to our web application, parse the data, and return 'ok'.
I've have enabled attribute routing in WebApiConfig.cs, have tested multiple different controller configurations, added "GlobalConfiguration.Configure(WebApiConfig.Register);" to my Global.asax.cs file, and made sure my classes are public.
Am I missing something? I've been troubleshooting this for several hours over multiple days and have not been able to figure it out.
In my postman request I am not sending any parameters, have the content type header set to jason, and am only including a sample SendGrid event in the body. I've verified the port number, and am not passing any authentication via http to our local application. The POST request is being sent to the following url: http://localhost:59998/api/sample
I've followed several stack overflow posts on similar issues and have made sure I'm not shooting myself in the foot (private classes, plural vs singular, api config settings).
My api config:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
I've removed most of the action methods to simplify the code for the controller. All I'm looking to do right now is to accept a POST, debug locally, and return ok. I've tried with multiple different class types and have back-tracked to just the most simple options possible.
I have breakpoints set in my controller and I've been troubleshooting with multiple testing variables, which I've removed to clean up the code (example: int test = 0).
namespace StickerAppWeb.Controllers
{
public class SampleController : ApiController
{
[HttpPost]
public string Index()
{
return "API method";
}
public void Post()
{
//int test = 0; //breakpoint here
}
}
}
My Global config:
namespace StickerAppWeb
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
Update1:
As a follow-up from yesterday, I also have this project published to Azure and get the same response from Postman when submitting a POST to the application in Azure. Is there any reason that the application is not finding that controller both locally, and in Azure?
Your code has no problem and should work. To send a post request to the api, you can follow the code below.
public string MYMethod()
{
string str= Task.Run(() => GetFromWeb).Result;
return str;
}
If you want to use the post method
private async Task<string> GetFromWeb()
{
HttpClient client = new HttpClient();
string Resturl = "domainname/api/sample/Index";
var response = await client.PostAsync(Resturl, null);
var result = await response.Content.ReadAsStringAsync();
return result;
}
If you want to use the get method
private async Task<string> GetFromWeb()
{
var client = new HttpClient();
var result = await client.GetAsync("domainname/api/sample/Index");
var response = await result.Content.ReadAsStringAsync();
return response;
}
I have to retrieve orders from my API made in NET Core.
The retrieval is made in an action of my MVC controller calling an endpoint of another NET Core APP using GET.
The API endpoint is the following one:
API:
[HttpGet("orders/orderSearchParameters")]
public IActionResult Get(OrderSearchParameters orderSearchParameters)
{
var orders = MenuService.GetMenuOrders(new GetMenuOrdersRequest { From = orderSearchParameters.From, To = orderSearchParameters.To, FoodProviderId = orderSearchParameters.FoodProviderId }).Orders;
return Ok(orders);
}
An action of my Web App MVC controller must call that endpoint, and for that this is the following code:
public IActionResult GetOrders(OrderSearchParametersModel orderSearchParameters)
{
var uri = string.Format(ApiUri + "menus/orders/");
using (HttpClient httpClient = new HttpClient())
{
var response = httpClient.GetStringAsync(uri);
if (response.IsCompleted)
{
var orders = JsonConvert.DeserializeObject<List<OrderModel>>(response.Result);
return Ok(orders);
}
else
{
return BadRequest();
}
}
}
What I can´t find is how can I serialize OrderSearchParametersModel to perform the GET operation with the HttpClient in the MVC controller.
In the attached code I do the GET without the incoming object.
How can I send this object using GET operation with HttpClient?
If you put all your parameters in the querystring, they will be translated into an OrderSearchParametersModel but they need to match this model properties names.
Under one colution file I have couple of projects.UI (ASP.Net mvc) project, A project for my rest services, A DTO project, A business logic project and Data Access. Now my services projects's controller needs to talk with my UI(ASP.Net MVC) project's controller and get the data entered in the form and send it to the database. Im quite unsure of the logic I should come up with inside the controller class of the UI project. The UI project sepratley has entity classes as well. Help needed!!
This is the POST method in my services controller
// POST api/Maintenance
[HttpPost]
public IHttpActionResult Post([FromBody]Maintenance maintenance)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
maintenanceLogic.Insert(maintenance);
return CreatedAtRoute("DefaultApi", new { id = maintenance.WorkID }, maintenance);
}
This is the method that will access the above methods uri.This method is in the controller class of my UI project. I came up with a logic.But I think its not correct.
[HttpPost, Route("/maintenance/CreateMaintenanceOrder")]
public PartialViewResult CreateMaintenanceOrder([FromBody] Maintenance Model)
{
Model.CheckItems = maintenanceViewModel.CheckItems;
Model.CrewMembers = maintenanceViewModel.CrewMembers;
Model.WorkID = ++SessionUtility.CurrentMaintenanceID;
try
{
var uri = "api/Maintenance/Post ";
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("http://localhost:8961");
Task<String> request = httpClient.GetStringAsync(uri);
Model = JsonConvert.DeserializeObject<List<Maintenance>>(request.Result);
}
maintenanceViewModel.MaintenanceOrders.Add(Model);
}
catch (AggregateException e)
{
}
maintenanceViewModel.MaintenanceOrders.Add(Model);
return PartialView("_Search", maintenanceViewModel);
}
You are trying to call a Post method using GetAsync as well as it seems you are not passing the Maintenance object required by Rest service method as input. Please try below -
string maintenanceData = JsonConvert.SerializeObject(Model); //considering you have to pass Model as input to Rest method
HttpResponseMessage response = httpClient.PostAsync(new Uri("http://localhost:8961" + "api/Maintenance/Post/"), new StringContent(maintenanceData));
I have an MVC project that is exposed externally. I have an internal Web API project.
For reasons beyond my control, I cannot expose the Web API project directly and I cannot add Web API Controllers to my MVC project.
I need to create an MVC Controller that will act as a proxy for a Web API Controller. I need the response from the MVC Controller to look as if the Web API was called directly.
What is the best way to accomplish this?
Is there a better approach than what I have so far?
How can I fix the error that I am getting?
Here is what I have so far:
MyMVCController
[HttpGet]
public HttpResponseMessage GetData(HttpRequestMessage request)
{
...
var response = proxy.GetData();
return request.CreateResponse();
}
MyProxyClass
public HttpResponseMessage GetData()
{
...
return HttpRequest(new HttpRequestMessage(HttpMethod.Get, uri));
}
private HttpResponseMessage HttpRequest(HttpRequestMessage message)
{
HttpResponseMessage response;
...
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(120);
response = client.SendAsync(message).Result;
}
return response;
}
In the MVC Controller, I am getting an InvalidOperationException on the request.CreateResponse() line. The error says:
The request does not have an associated configuration object or the provided configuration was null.
Any help would be greatly appreciated. I have searched Google and StackOverflow but I haven't been able to find a good solution for creating this proxy between MVC and Web API.
Thanks!
You can do it by just creating some JsonResult action in your controller which will return result of calling web API.
public class HomeController : Controller
{
public async Task<JsonResult> CallToWebApi()
{
return this.Content(
await new WebApiCaller().GetObjectsAsync(),
"application/json"
);
}
}
public class WebApiCaller
{
readonly string uri = "your url";
public async Task<string> GetObjectsAsync()
{
using (HttpClient httpClient = new HttpClient())
{
return await httpClient.GetStringAsync(uri);
}
}
}
So there is a heap of examples around but finding ones that are relevant to the rtm bits seems to be a little harder to find.
I have 2 projects one is an WebApi & the other is MVC4 .net 4.5 application.
I want to make a make an update to an item
I have a controller within my API that does something like
[HttpPut]
public MyModel Update(MyModel model)
{
//make update
return model;
}
Is this correct? should I be using a HttpResponseMessage instead of just using my MyModel class? I want to return the correct httpstatus details as much as possible as I am wanting to open up this api to 3rd parties not just my application
Calling this api from my mvc application from my controller how do I do this?
The beste way is to use HttpResponseMessage like this:
[HttpPut]
public HttpResponseMessage Update(MyModel model)
{
if(notfound)
{
return this.Request.CreateResponse(HttpStatusCode.NotFound);
}
//make update
return this.Request.CreateResponse<MyModel>(HttpStatusCode.OK, Model);;
}
I mostly use EasyHttp if I want want to call a WebApi method from my MVC app:
var model = new ExpandoObject(); // or use a stronly typed class.
model.Id = 1,
model.Name = "foo"
var http = new HttpClient();
http.Post("url", model, HttpContentTypes.ApplicationJson);
If you want to respond with httpstaus code you have to return HttpResponseMessage.
You may choose to have a common method returning your BOs and call it from the Action and from your other mvc application code. Then your rest calls would always be wrapped with a status code and other calls get an object.
[HttpPut]
public MyModel Update(MyModel model)
{
return base.Request.CreateResponse<MyModel>(HttpStatusCode.OK, UpdateModel(model));;
}
[NonAction]
internal MyModel UpdateModel(MyModel model)
{
//make update
return model;
}