I have one API endpoint for login. Once we enter correct username and password then we are getting following details :
{
"accessToken": "Some_Value",
"accessTokenExpireAt": "Some_Value",
"refreshToken": "Some_Value",
"userID": "Some_Value"
}
Once user click on Submit button then below code will be executed.
public async Task<ActionResult> Index(LoginModel loginModel)
{
using (var client = new HttpClient())
{
string url = "URL_ENDPOINT_FOR_LOGIN";
client.DefaultRequestHeaders.Accept.Clear();
var response = await client.PostAsJsonAsync(url, loginModel);
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync();
//Here I am getting all values in JSON in result. Now I want to redirect to user to profile page and on that page need to call another API which will fetch all details of that user. In Result I am getting userId.
}
else
{
ModelState.AddModelError("", "Wrong details");
}
}
return View(loginModel);
}
Now I have one more API end point, when we call that then we are getting that user details like firstname, email id and so on.
I need to display that all user details in View, but only if that accessToken is coming.
So I want to know how I can pass this accessToken and userId to another action and all API url from there.
I did something similar before in a Web API.
Can you try this in your API method:
return RedirectToAction("RedirectedAction",
"ControllerName",
new { val = value1, ..., val = valueN });
Where value1, ... valueN are the parameters you pass onto the next API method.
The redirected method stub is then:
public async Task<ActionResult> RedirectedAction(
string value1, string value2, ...)
{
...
}
(Adjust your parameter types as required.)
You can simply call by parameter and send json string to the action and in that action you can deserialize the Json string.
public async Task<ActionResult> Index(LoginModel loginModel)
{
using (var client = new HttpClient())
{
string url = "URL_ENDPOINT_FOR_LOGIN";
client.DefaultRequestHeaders.Accept.Clear();
var response = await client.PostAsJsonAsync(url, loginModel);
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync();
string JsonString=result.Result;
return RedirectToAction("YourAction", new { YourParam= JsonString });
}
else
{
ModelState.AddModelError("", "Wrong details");
}
}
return View(loginModel);
}
In the Other Action Your Can simply Deserialize using NewtonSoft.Json if not included you can download from nuget.
public ActionResult YourAction(string YourParam)
{
YourClass clsObj= JsonConvert.DeserializeObject<YourClass>(JsonString /*YourParam in this case*/);
// Do your Work Here
}
Also you can remove async task from your action to load your view after completing API request.
Just add
System.Threading.Tasks.Task<HttpResponseMessage> response = client.PostAsJsonAsync(url, loginModel);
response.Wait();
in place of
var response = await client.PostAsJsonAsync(url, loginModel);
Also in action prototype
public ActionResult Index(LoginModel loginModel)
Related
I need to save the changes I make in my model through API call in my database. I have checked my API is working fine when I am running it individually on Web. But its giving me an error StatusCode: 405, ReasonPhrase: 'Method Not Allowed'. I am trying to send and object and trying to see whether the request made was completed or not. When I am trying to debug it, it is not sending hit on my API controller.
Here is my model class:
public class Customer
{
[Required]
public Guid CustomerId { get; set; }
public int Age { get; set; }
public int Phone { get; set; }
}
PUT Method in API:
[HttpPut]
[Route("api/[controller]/{customer}")]
public IActionResult EditCustomer(Customer customer)
{
var cust = _customerData.EditCustomer(customer);
if (cust == string.Empty)
{
return Ok();
}
else
{
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
The method I am using in project to call API:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiBaseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")
);
var sum = await client.PutAsJsonAsync("api/Customer/", customer);
if (sum.StatusCode == System.Net.HttpStatusCode.OK)
{
return RedirectToActionPermanent(actionName: "SingIn");
}
else
{
TempData["msg"] = "There is an error";
return View();
}
where baseaddress= {https://localhost:44398/}
EditCustomer Method
public string EditCustomer(Customer customer)
{
try
{
var pro = _customerContext.Customer.Where(e => e.CustomerId == customer.CustomerId).FirstOrDefault();
pro.Age = customer.Age;
pro.Phone = customer.Phone;
pro.Name = customer.Name;
_customerContext.Entry(pro).State = EntityState.Modified;
_customerContext.SaveChanges();
}
catch(Exception e)
{
return e.Message;
}
return string.Empty;
}
You need to fix your action route by removing {Customer}, since you send customer in request body, not as a route value
[Route("~/api/Customer")]
and request
var sum = await client.PutAsJsonAsync("/api/Customer", customer);
or better fix the acttion route name to meaningfull
[Route("~/api/EditCustomer")]
and
var sum = await client.PutAsJsonAsync("/api/EditCustomer", customer);
AsJsonAsync sometimes causes problems
try this code
var json = JsonSerializer.Serialize(customer);
//or if you are using Newtonsoft
var json = JsonConvert.SerializeObject(customer);
var contentData = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PutAsync("/api/Customer", contentData);
if (response.IsSuccessStatusCode)
return RedirectToActionPermanent("SingIn");
else
{
TempData["msg"] = "There is an error";
return View();
}
but IMHO I would prefer to use
client.PostAsync("/api/EditCustomer", contentData);
instead of Put.
and added [FromBody] to action
[HttpPost("~/api/EditCustomer")]
public IActionResult EditCustomer([FromBody] Customer customer)
I am no pro in web APIs but I suspect it could be due to the fact that the API expects customer to be in request URL.
Try and change the API route to [Route("api/[controller]")]
This could've been a comment but I don't have enough reputation :)
I meet a problem because of my inexperience managing Threads.
I have this Action bellow :
public static async Task<joueurs> loadjoueurs(int id)
{
joueurs EmpInfo = new joueurs();
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri("http://www.myWebApi.fr/api/");
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("joueurs?id=" + id);
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var EmpResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
EmpInfo = JsonConvert.DeserializeObject<joueurs>(EmpResponse);
return EmpInfo;
}
return null;
}
it s just client to get my data from a webApi (no ssl no authentication, when I test it I receive the right values)
but when I make a call using the function above (in my asp.net website) .... it stay stucked at the HttpResponseMessage = await .... eternally.
In my webApi I have two functions same name but different parameters .
public async Task<IHttpActionResult> Getjoueur(int iduser, int idsport)
and
public async Task<IHttpActionResult> Getjoueur(int id)
So I am don't know where the problem comes from.
(sequel) Here is the place where I call the Task :
public SuperModel(int id)
{
this.joueur = Repojoueurs.loadjoueurs(id).Result;
/* this.classificationSport = Repoclassificationsport.loadclassificationsport().Result;
...
*/
}
And then my Supermodel is instantiated here in my Home controller :
public ActionResult Index(int id)
{
SuperModel superModel = new SuperModel(id);
return View(superModel);
}
Can you try not to use the async and wait. Around three changes like below
public static HttpResponseMessage loadjoueurs(int id)
{
HttpResponseMessage Res = client.GetAsync("joueurs?id=" + id);
return Request.CreateResponse(HttpStatusCode.OK,EmpInfo, "application/json");
}
I have one question, which connected with redirecting and auth policies.
Let's have one controller, which allow Anonymous method like this:
[Route("Authorization")]
[Authorize]
public class AuthorizationController : Controller
{
...
[HttpPost]
[Route("AddUser")]
[AllowAnonymous]
public async Task<IActionResult> AddUser()
{
return await Task.Run<ActionResult>(() =>
{
return RedirectToAction("Post", "Proxy");
});
}
}
Second controller has Post method, which needs authorization
[Authorize]
public class ProxyController : Controller
{
...
[HttpPost]
public async Task Post()
{
var uri = new Uri(UriHelper.GetEncodedUrl(Request));
var routedUri = NewRouteBuilder(uri);
var client = new HttpClient();
var response = await client.PostAsync(routedUri, new StreamContent(Request.Body));
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType?.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
}
If I use this code, I get 401 error in AuthorizationController, when I call AddUser.
Both these controllers are in one project. How it's possible to redirect on action in this case (which allow pass to action only authorized users or calls from ProxyController)?
Thank you.
I am trying to use the account controller in the MVC project to call the register account method in a WebApi 2 account controller. All works fine but I cant figure out how to return errors back to the MVC project such as: "password must contain Upper case and lower case" etc.
ASP.NET MVC account controller register:
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var response =
await
ApiRequest.PostAsync(String.Format("{0}/api/v1/account/register", "http://localhost:12345"), model);
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index", "Home");
}
// Add errors
}
// If we got this far, something failed, redisplay form
return View(model);
}
ApiRequest class:
public static class ApiRequest
{
public static async Task<HttpResponseMessage> PostAsync(string uri, object item)
{
StringContent content = new StringContent(await Json.SerializeAsync(item));
content.Headers.ContentType = new MediaTypeHeaderValue("text/json");
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.PostAsync(new Uri(uri), content);
}
}
public static async Task<HttpResponseMessage> GetAsync(string uri)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.GetAsync(new Uri(uri));
}
}
public static async Task<HttpResponseMessage> PutAsync(string uri, object item)
{
StringContent content = new StringContent(await Json.SerializeAsync(item));
content.Headers.ContentType = new MediaTypeHeaderValue("text/json");
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.PutAsync(new Uri(uri), content);
}
}
public static async Task<HttpResponseMessage> DeleteAsync(string uri, object id)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.DeleteAsync(new Uri(String.Format("{0}/{1}", uri, id)));
}
}
}
public static class HttpResponseMessageExtensions
{
public static async Task<T> DeserialiseContentAsync<T>(this HttpResponseMessage message)
where T : class
{
return await Json.DeserialiseAsync<T>(await message.Content.ReadAsStringAsync());
}
}
Web API 2 account controller register:
//
// POST: /Account/Register
[AllowAnonymous]
[Route("Register")]
[HttpPost]
public async Task<IHttpActionResult> Register(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser
{
UserName = model.Username,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName
};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
if (result.Errors != null)
{
foreach (string error in result.Errors)
{
ModelState.AddModelError("", error);
}
return BadRequest(ModelState);
}
return BadRequest();
}
// Send email verification
//string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
//var callbackUrl = new Uri(Url.Link("ConfirmEmail", new { userId = user.Id, code = code }));
//await
// UserManager.SendEmailAsync(user.Id, "Confirm your account",
// "Please confirm your account by clicking here");
Uri locationHeader = new Uri(Url.Link("GetUserById", new { id = user.Id }));
return Created(locationHeader, user);
}
GetErrorResult code:
private IHttpActionResult GetErrorResult(IdentityResult result)
{
if (result == null)
{
return InternalServerError();
}
if (!result.Succeeded)
{
if (result.Errors != null)
{
AddErrors(result);
}
if (ModelState.IsValid)
{
// No ModelState errors are available to send, so just return an empty BadRequest.
return BadRequest();
}
return BadRequest(ModelState);
}
return null;
}
private void AddErrors(IdentityResult result)
{
foreach (string error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
Im new to MVC and WebApi and have mainly been following tutorials, this is assume is basic stuff but i cant find a solution anywhere. I have separated the WebApi from the project intentionally so I can learn how these processes work better.
I'd like the solution not to be in javascript.
I'm assuming all subsequent requests will need to have a bearer token attached to the httpclient but i think this will be in another question.
Thanks in advance for any help on this
All works fine but I cant figure out how to return errors back to the MVC project such as: "password must contain Upper case and lower case" etc.
You can send custom message in BadRequest() method overload. When you perform custom validation on Model just return a custom message.
if (ValidatePasswordPolicy(model.Password))
{
return BadRequest("password must contain Upper case and lower case.");
}
Update:
There's a difference in ModelState validation of Asp.net mvc app and WebAPI. When you're using ModelState to put errors in WebAPI then you have to properly handle the response. Proper handling means the response is succeeded or not. If not then are there ModelState errors or other error. Below snippet will show you how to deserialize the error to anonymous object and check if there are model state errors or other error. In case of ModelState errors you can simply add them to your Asp.net mvc Model and return the view with model to update UI with errors.
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsAsync<T>();
}
else
{
var httpErrorObject = await response.Content.ReadAsStringAsync();
var anonymousErrorObject =
new { message = "", ModelState = new Dictionary<string, string[]>() };
// Deserialize:
var deserializedErrorObject =
JsonConvert.DeserializeAnonymousType(httpErrorObject, anonymousErrorObject);
// Check if there are actually model errors
if (deserializedErrorObject.ModelState != null)
{
var errors =
deserializedErrorObject.ModelState
.Select(kvp => string.Join(". ", kvp.Value));
for (int i = 0; i < errors.Count(); i++)
{
// Add errors to ModelState in Asp.net mvc app
}
}
// Othertimes, there may not be Model Errors:
else
{
var error =
JsonConvert.DeserializeObject<Dictionary<string, string>>(httpErrorObject);
foreach (var kvp in error)
{
// Now this is not model error so you can throw exception
// or any custom action what ever you like
}
}
}
There's a detailed article about handling errors from WebAPI in Asp.net mvc App here.
I have a situation where I have an MVC Controller that I'm calling a WebApi POST method. I'm passing a DTO as a parameter... The WebApi method is hit, but the parameter is null. What am I missing?
Thanks!
MVC Controller:
[Authorize]
public async Task<ActionResult> Index()
{
Permissions service = new Permissions();
ViewBag.Title = "Deployment Manager";
string uri = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiRestfulUrl"] + "/Permissions";
using (var client = new HttpClient())
{
var permissions = new PermissionsDTO() { UserName = "rherhut" };
var response = await client.PostAsJsonAsync(uri, permissions);
if (response.IsSuccessStatusCode)
{
// Get response data how??
}
}
return View();
}
WebAPI POST Method:
public HttpResponseMessage Post(HttpRequestMessage request, [FromBody]PermissionsDTO permissions)
{
var data = repository.HasAdminRights(permissions.UserName); // permissions.UserName is null
var response = new ApiResponseCreator<PermissionsDTO>();
return response.FormatReturnData(request, data);
}
I resolved the problem by decorating the dto class with a "Serialize" attribute and for some reason this worked.