Call async Task<> from controller in ASP.NET MVC - asp.net-mvc

I have a Library wrapper OVH API and i try to call a function for get my consumer key in a ASP.NET MVC project. It works in a Console project application but the method never response in my Controller : the method in the Library
public async Task<CredentialsResponse> RequestCredential(IEnumerable<AccessRule> accessRules, string redirectUrl = null)
{
Ensure.NotNull("accessRules", accessRules);
CredentialsRequest cmd = new CredentialsRequest();
cmd.AccessRules.AddRange(accessRules);
cmd.Redirection = redirectUrl;
if (cmd.AccessRules.Count == 0)
throw new ArgumentException("You must specify at least one accessRule");
return await RawCall<CredentialsResponse>(HttpMethod.Post, "/auth/credential", cmd;
}
and i call in the controller :
public ActionResult Index()
{
Information infosClient = new Information();
OvhApiClient api = new OvhApiClient("", "", OvhInfra.Europe);
CredentialsResponse response = api.RequestCredential(new[]{
new AccessRule{ Method = "GET", Path = "/*"},
new AccessRule{ Method = "PUT", Path = "/*"},
new AccessRule{ Method = "POST", Path = "/*"},
//new AccessRule{ Method = "DELETE", Path = "/*"},
}).Result;
api.ConsumerKey = response.ConsumerKey;
infosClient.ConsumerKey = api.ConsumerKey;
return View(infosClient);
}
I already tried quite a lot of things without success (put the call in a method async for example).
Thanks in advance for your help

Make the controller action async:
public async Task<ActionResult> Index()
{
...
CredentialsResponse response = await api.RequestCredential(...);
...
}

Related

Redirect to specified action if requested action was not found

How can I redirect Action which is not found in controller into another action within the same controller? Let's say that file abc.txt is requested via http://localhost:5000/Link/GetFile/abc.txt. My controller correctly serving that file. But now, i need to handle request such as http://localhost:5000/Link/Document/abc. Of course there is no any action matched to Document so I need to invoke function Error within the same controller (including id from original request).
I tried to solve this with StatusCodePagesWithReExecute function but then my File action is not working (each request goes directly to Error function).
I have following controller:
public class LinkController : ControllerBase
{
public IActionResult GetFile(string id)
{
return DownloadFile(id);
}
public IActionResult Error(string id)
{
return File("~/index.html", "text/html");
}
private FileResult DownloadFile(string fileName)
{
IFileProvider provider = new PhysicalFileProvider(#mypath);
IFileInfo fileInfo = provider.GetFileInfo(fileName);
var readStream = fileInfo.CreateReadStream();
return File(readStream, "text/plain");
}
}
and startup configuration:
app.UseDefaultFiles();
app.UseStaticFiles(new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "application/octet-stream",
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}"
);
});
Any clues how to solve this problem?
Regards
You can use UseStatusCodePages to achieve a simple redirection whenever there's a 404. Here's what it looks like:
app.UseStatusCodePages(ctx =>
{
if (ctx.HttpContext.Response.StatusCode == 404)
ctx.HttpContext.Response.Redirect("/Path/To/Your/Action");
return Task.CompletedTask;
});
Just add this somewhere above UseMvc.
EDIT:
I´m sorry, my first answer was not correct.
IRouteCollection router = RouteData.Routers.OfType<IRouteCollection>().First();
with this, you can match an url to controller action
Create HttpContext for testing (example with injection)
private readonly IHttpContextFactory _httpContextFactory;
public HomeController(
IHttpContextFactory httpContextFactory)
{
_httpContextFactory = httpContextFactory;
}
Create the context with values
HttpContext context = _httpContextFactory.Create(HttpContext.Features);
context.Request.Path = "/Home/Index";
context.Request.Method = "GET";
Check route
var routeContext = new RouteContext(context);
await router.RouteAsync(routeContext);
bool exists = routeContext.Handler != null;
Further reading: https://joonasw.net/view/find-out-if-url-matches-action

Unable to send data from actionResult to asyn mothod in mvc

This is my controller actionReslt
[ValidateAntiForgeryToken()]
public ActionResult PaymentDetails(PaymentViewModel payment)
{
PaymentModel paymentModel = new PaymentModel();
// AutoMapper.Mapper.CreateMap<PaymentModel, PaymentViewModel>();
if (ModelState.IsValid)
{
CreditCardDetailsModel creditCardDetailsModel = new CreditCardDetailsModel();
creditCardDetailsModel.SecurityId = payment.SecurityId;
creditCardDetailsModel.ExpiryDate = payment.Month + payment.Year;
creditCardDetailsModel.CardNumber = payment.CardNumber;
paymentModel.CreditCardDetails = creditCardDetailsModel;
return RedirectToAction("Payment",paymentModel);
}
return View("FlightBooking");
}
and this is my async method
public async Task<JsonResult> Payment(PaymentModel model)
{
CreateFormOfPaymentReplyModel response = new CreateFormOfPaymentReplyModel();
resource = Constants.Payment;
response = await Post<CreateFormOfPaymentReplyModel>(model);
resource = Constants.PnrConfirm;
var pnrConformStatus = await Get<PNRConfirmResponseModel>();
return new JsonResult { Data = new { status = false, message = response } };
}
and i want to return to Payment method with paymentObject if it is valid but PaymentModel is returning null data and it is showing the error as
This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet
Have you tried passing allowget to your return variable?
i.e:
return Json( new { status = false, message = response }, JsonRequestBehavior.AllowGet);
Hope this helps.

Troubleshoot MVC model binding failure - argument is null in controller

I am trying to POST an object from a WebJob to an MVC 4 controller. I am using Entity Framework. In the controller, I cannot get the object to bind properly (the argument is null). I have looked at many tutorials and it seems like my code should work.
Model (does this need to be in a specific namespace for EF to find it?):
public class CreateListingObject
{
public Listing listing;
public List<GalleryImage> images;
public CreateListingObject()
{
listing = new Listing();
images = new List<GalleryImage>();
}
}
public struct GalleryImage
{
public string picURL;
public string caption;
}
POST:
public void PostListing(CreateListingObject o)
{
Console.WriteLine("Posting listing: {0}", o.listing.Title);
HttpClient _httpClient = new HttpClient();
Uri uri = new Uri(_serviceUri, "/Automaton/CreateTestListing");
string json = BizbotHelper.SerializeJson(o);
HttpResponseMessage response = BizbotHelper.SendRequest(_httpClient, HttpMethod.Post, uri, json);
string r = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
}
SendRequest (thank you Azure search samples):
public static HttpResponseMessage SendRequest(HttpClient client, HttpMethod method, Uri uri, string json = null)
{
UriBuilder builder = new UriBuilder(uri);
//string separator = string.IsNullOrWhiteSpace(builder.Query) ? string.Empty : "&";
//builder.Query = builder.Query.TrimStart('?') + separator + ApiVersionString;
var request = new HttpRequestMessage(method, builder.Uri);
if (json != null)
{
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
return client.SendAsync(request).Result;
}
Controller Action fragment (o is an empty object here):
[HttpPost]
public ActionResult CreateTestListing(CreateListingObject o)
{
Listing li = o.listing;
I have confirmed that if I post a simple object using the same code, everything works as expected.
Instead of sending a CreateListingObject in PostListing, I send this instead:
var test = new
{
data = "hi mom"
};
And change my action to, then the argument gets bound and I get valid data:
[HttpPost]
public ActionResult CreateTestListing(string data)
{
I have also checked the serialization of my CreateListingObject in the WebJob, and it is fully populated as I expect. This leads me to suspect that I am falling afoul of the default ModelBinder.

How to show MVC logged in username with AngularJs

I am using MVC 5 / WebApi 2 and AngularJs. I want to display the Logged in username in my view. I know how to display that information using razor but how can I do it with Angular? So basically I need to do this with Angular.
<span >Logged In As: #Html.ActionLink(User.Identity.GetUserName(), "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage", #style = "color:white;float:right" })</span>
apiUserController
public class apiUserController : ApiController
{
// GET api/<controller>
public List<ApplicationUser> Get()
{
using (var context = new ApplicationDbContext())
{
List<ApplicationUser> users = new List<ApplicationUser>();
users = context.ApplicationUsers
.ToList();
return users;
}
}
}
Updated
public IHttpActionResult Get()
{
using (var context = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
var user = context.FindById(User.Identity.GetUserId());
var loggedInUser = user.UserName;
return Ok(loggedInUser);
}
}
you'll need to create a service that returns your user information
angular.module('app').factory('Authentication', function ($resource) {
var resource = $resource('/user', {}, {
query: {
method: 'GET',
cache: true
}
});
return resource.get().$promise;
});
* note that you'll need to create and endpoint that will send you the user data as json using web api
once you got it done you'll be able to use it in any controller (let's assume you have a homecontroller, it could be a headercontroller or any other)
angular.module('app').controller('HomeController', ['$scope', 'Authentication', function ($scope, Authentication) {
$scope.authentication = Authentication;
}]);
then use it in your view like:
<span >Logged In As: {{authentication.user.username}} </span>
EDIT:
your api controller as you suggested could be like
public HttpResponseMessage Get()
{
var userId = getCurrentUserId(); //something like that
using (var context = new ApplicationDbContext())
{
ApplicationUser user = new ApplicationUser();
user = context.ApplicationUsers.SingleOrDefault(x=>x.id==userId);
return user;
}
}
try to read http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
for routing try to read this article (I guess you are using web api 2)
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
If you want to cheat a little, you can do this in <head> in your _Layout:
<script type="text/javascript">
(function(myApp) {
myApp.username = "#User.Identity.GetUserName()";
//optional
myApp.otherStuff = "#moreMvcStuff";
})(window.myApp = window.myApp || {});
</script>
Then start your angular app like this:
(function (myApp) {
"use strict";
//var app = set up your angular app
app.run(["$rootScope",
function ($rootScope) {
$rootScope.appSettings = {
username: myApp.username
};
}
]);
})(window.myApp = window.myApp || {});
What you are doing is creating a single value on the window called myApp (or name it whatever you like) and passing it into your IIFE. This gives you access to it inside your angular script, bot only in that on block. So if you want it to stick around, you need to put it in a service or your rootScope.
In the app.run block, you can stick it in your rootScope or wherever you want it.
Now in your views you can display it with {{appSettings.username}}.
I call this "cheating" because it's specifically for MVC or webforms and it's not the "angular way". If you ever migrated to a fully agnostic html/js client (no asp.net mvc) and web APIs, you'd need to do what is in the currently-accepted answer.

Calling RedirectToAction

I have a form that calls this action to build the CompareEvents page:
[HttpPost]
public ActionResult CompareEvents(int[] EventsList, bool showIndex, bool showFRN, bool showProvider)
{
var viewModel = new EventsListViewModel
{
Events = EventsList,
ShowFRN = showFRN,
ShowIndex = showIndex,
ShowProvider = showProvider
};
return View(viewModel);
}
in the CompareEvents view there is another form that allows the user to update information:
[HttpPost]
public ActionResult UpdateSolution(IEnumerable<Solution> sol)
{
//update solution code
int[] eventList = { '85' };
return RedirectToAction("CompareEvents", new { EventsList = eventList, showIndex = true, showFRN = true, showProvider = true });
}
When this information is update, I would like to reload the page. I plan on doing this by calling the CompareEvents action again, however my stacktrace is saying that A public action method 'CompareEvents' was not found on controller
How can I accomplish this?
You cannot redirect to an action that is marked [HttpPost]. RedirectToAction uses a GET.
Source:
Returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action.
Reference.

Resources