How to send the Expression Parameter to Web Api - c#

I have a Web Api project.
This method calling the web api.
public User GetUser(Expression<Func<User, bool>> where)
{
HttpClient Client = new HttpClient();
Client.BaseAddress = new Uri("http://192.168.2.139:23283/api/user/");
var responseTask = Client.GetAsync("GetUserByExpression?id=" + where);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<User>();
readTask.Wait();
return readTask.Result;
}
else
{
return new User();
}
}
var test=GetUser(p=>p.Id==1);
After using the method, it enters the api but the parameter is null.
[HttpGet]
[Route("GetUserByExpression")]
public HttpResponseMessage GetUserByExpression(Expression<Func<User, bool>> where)
{
try
{
var user = repoUser.Find(where);
if (user != null)
{
return Request.CreateResponse(HttpStatusCode.OK, user);
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound, "User not found.");
}
}
catch (Exception exp)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, exp.Message);
}
}
The "where" parameter is null when I call the Get method. Therefore, I can not make a healthy operation. What can I do to prevent this parameter from going to null?

Related

How can I consume a FileContentResult returned from an .Net Core api inside a .Net WebApi?

I am trying something similar posted [here][1]
[1]: .Net Core Receive FileContentResult from one API to Another This is what I am trying and I think I am very close,just that not sure how to receive the in the receiver Api a response which is being sent by the Producer Api as FileContentResult, any help will be appreciated.
My effort is listed below:
//Source API-working well
[HttpPost("download")]
public async Task<FileContentResult> ExportApplicationUsers2(ExportApplicationUsersQuery command)
{
try
{
var filePath = await Mediator.Send(command);
return File(System.IO.File.ReadAllBytes(filePath), "application/octet-stream");
}
catch (Exception ex)
{
throw ex.InnerException;
}
}
//Controller in Consumer Api
public async Task<Result<FileContentResult>> ExportApplicationUsers(ExportUsersRequest model)
{
try
{
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.PostFileAsync("/users/download", JsonConvert.SerializeObject(model));
return response;
//return File(System.IO.File.ReadAllBytes(response), "application/octet-stream");
}
catch (Exception ex)
{
_logger.Error(ex.Message, ex);
return new Result<FileContentResult>(new List<string> { ex.Message });
}
}
The Actual PostFileAsync() which I need to fix first:
public async Task<FileContentResult> PostFileAsync(string uri, object data)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
if (data != null)
{
request.Content = new StringContent(data.ToString(), Encoding.UTF8, "application/json");
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
var response = default(FileContentResult);
var actionTask = _httpClient.SendAsync(request)
.ContinueWith(responseTask =>
{
FileStreamResult resposneMessage = responseTask.Result;
response = (FileContentResult)resposneMessage.ReadAsStreamAsync().Result;//This is where I need help as its not able to convert into FileContentResult!
});
actionTask.Wait();
return response;
}

How To Pass ErrorMessage From API to MVC In Asp.Net Core

I've created an API. When you face an error, It shows you the type of error with it's message. But When I try to use that API in my MVC project, It just shows the type of error. I want to see the message in Modelstate.AddModelError
Here is API controller for Login
[HttpPost("login")]
public async Task<IActionResult> LoginUser([FromBody] UserDtoLogin user)
{
var userToRetrieve = await _applicationDbContext.Users.FirstOrDefaultAsync(u => u.UserName == user.UserName);
if (userToRetrieve == null)
{
ModelState.AddModelError("username", "Such a user doesn't exists! Enter the correct username please");
return NotFound(ModelState);
}
if (!_userRepository.VerifyPasswordHash(user.Password, userToRetrieve.PasswordHash, userToRetrieve.PasswordSalt))
{
ModelState.AddModelError("password", "Wrong Password!");
return BadRequest(ModelState);
}
await _userRepository.Login(userToRetrieve);
return Ok(user);
}
Here is MVC Controller for Login
[HttpPost]
public async Task<IActionResult> Login(User user)
{
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:42045/api/user/login");
if (user != null)
{
request.Content = new StringContent(JsonConvert.SerializeObject(user),
System.Text.Encoding.UTF8, "application/json");
}
var client = _clientFactory.CreateClient();
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
ViewBag.StatusCode = System.Net.HttpStatusCode.OK;
var apiString = await response.Content.ReadAsStringAsync();
user = JsonConvert.DeserializeObject<User>(apiString);
}
else
{
ViewBag.StatusCode = response.StatusCode;
}
return View(user);
}
I write a simple demo to show how to pass ErrorMessage From API to MVC In Asp.Net Core, you can reference to it.
API
[Route("api/[controller]")]
[ApiController]
public class LoginController : ControllerBase
{
//For testing convenience, I use hard code here
List<UserDtoLogin> context = new List<UserDtoLogin>
{
new UserDtoLogin
{
UserName = "Mike"
},
new UserDtoLogin
{
UserName = "Jack"
},
new UserDtoLogin
{
UserName = "Lily"
}
};
[HttpPost("login")]
public IActionResult LoginUser([FromBody] UserDtoLogin user)
{
var userToRetrieve = context.FirstOrDefault(u => u.UserName == user.UserName);
if (userToRetrieve == null)
{
return BadRequest("Such a user doesn't exists! Enter the correct username please");
}
//your logic.....
return Ok();
}
}
MVC/Controller
[HttpPost]
public async Task<IActionResult> Privacy(UserDtoLogin todoItem)
{
var todoItemJson = new StringContent(JsonSerializer.Serialize(todoItem),Encoding.UTF8,Application.Json);
using var httpResponseMessage = await _httpClient.PostAsync("your api url", todoItemJson);
var errormessage = httpResponseMessage.Content.ReadAsStringAsync().Result;
return View(todoItem);
}
Then you can see it can receive the errormessage successfully.

Suggestion on how to implement conditional dependency?

I have two classes implementing the interfaces. Both the classes execute place search functionalities. Also in both the classe constructor there is a common functionality depending on the classes
Google : IGoogle
public NearBySearch(IWebPortalApiClient webPortalApiClient)
{
var serverString = ConfigHelper.GetAppSetting(ConfigConstants.APIServerType);
var server = (WebPortalServer)Enum.Parse(typeof(WebPortalServer), serverString, true);
this._webPortalApiClient = webPortalApiClient;
this.GoogleKey = $"&key={ConfigHelper.GetAppSetting(ConfigConstants.Googlekey)}";
this._webPortalApiClient.Init(server);
}
Yelp : IYelp
public BusinessSearch(IWebPortalApiClient webPortalApiClient)
{
var serverString = ConfigHelper.GetAppSetting(ConfigConstants.APIServerType);
var server = (WebPortalServer)Enum.Parse(typeof(WebPortalServer), serverString, true);
this._webPortalApiClient = webPortalApiClient;
this._webPortalApiClient.AccessToken = ConfigHelper.GetAppSetting(ConfigConstants.YelpApiKey);
this._webPortalApiClient.Init(server, this._webPortalApiClient.AccessToken);
}
In case of google we have to send key as a query parameter where as in Yelp we have to send key as Authorization header
In API Controller I am injecting Both
IGoogle
private IYelpController _yelpController;
private IGoogleController _googleController;
public PlaceSearchController(IYelpController yelpController, IGoogleController googleController)
{
this._yelpController = yelpController;
this._googleController = googleController;
}
The function that we are callin in API is like
[HttpGet]
public async Task<IHttpActionResult> GetAllBusiness(int web, decimal latitude, decimal longitude, string radius = null)
{
try
{
if (web == (int)PlaceSearch.Yelp)
{
var result = await _yelpController.GetAllBusiness(latitude, longitude, radius);
var response = new Response<Common.Models.Yelp.Yelp>(ResponseConstants.Success, ResponseConstants.SuccessMessage, result);
return Ok(response);
}
else if(web == (int)PlaceSearch.Google)
{
if(radius == null)
{
throw new Exception();
}
var result = await _googleController.GetAllNearByPlaces(latitude, longitude, radius);
var response = new Response<Common.Models.Google.Google>(ResponseConstants.Success, ResponseConstants.SuccessMessage, result);
return Ok(response);
}
throw new Exception();
}
catch (Exception ex)
{
var response = new Response<Object>(ResponseConstants.Forbidden, ResponseConstants.FailureMessage, null);
return Ok(response);
}
}
The issue I am facing is below function is called in both the constructor
public IWebPortalApiClient Init(WebPortalServer server, string accessToken = null)
{
WebPortalServer = server;
AccessToken = accessToken;
return this;
}
When Yelp is executed asses token is passed which is as expected but at the same time we dont pass access token in Google which sets the access token to null.
Because of this issue I am not able to call Yelp API as it does not find the access token.
Is there a way that we inject dependency on conditional basis. We are using Unity Container for this.
Web Portal Client
public class WebPortalApiClient : IWebPortalApiClient
{
public WebPortalServer WebPortalServer { get; set; }
public string AccessToken { get; set; }
private string ServerUrl
{
get
{
switch (WebPortalServer)
{
case WebPortalServer.Dev:
return null;
case WebPortalServer.QA:
return null;
case WebPortalServer.Demo:
return null;
case WebPortalServer.Production:
return null;
case WebPortalServer.Localhost:
return "http://localhost:63695/";
case WebPortalServer.Integration:
return null;
case WebPortalServer.UAT:
return null;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public async Task<HttpContent> InvokeApi(string path, HttpAction action, HttpContent content = null, TimeSpan? overrideTimeout = null, string externalServer = null)
{
var sUrl = externalServer == null ? ServerUrl : externalServer;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(sUrl);
if (overrideTimeout.HasValue)
{
client.Timeout = overrideTimeout.Value;
}
//this.Log("Connecting to {0} Api at {1}".Fmt(WebPortalServer, ServerUrl));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", AccessToken);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response;
switch (action)
{
case HttpAction.Get:
response = await client.GetAsync(path);
break;
case HttpAction.Post:
response = await client.PostAsync(path, content);
break;
case HttpAction.Put:
response = await client.PutAsync(path, content);
break;
case HttpAction.Delete:
response = await client.DeleteAsync(path);
break;
default:
throw new ArgumentOutOfRangeException("action", action, null);
}
return response.IsSuccessStatusCode ? response.Content : null;
}
}
public IWebPortalApiClient Init(WebPortalServer server, string accessToken = null)
{
WebPortalServer = server;
AccessToken = accessToken;
return this;
}
}

Server Not Finding Callback Url

I'm trying to implement OAuth2 with Fitbit in my WebAPI applications. I'm able to make the initial request to the fitbit api. But when it comes back the the server I'm getting a error saying that I cannot find the callback url
OAuthController
[HttpPost]
public async Task<HttpResponseMessage> Authorize(UserAuthRequestDTO request)
{
if (string.IsNullOrEmpty(request.PatientID) || string.IsNullOrEmpty(request.Provider))
Request.CreateResponse(HttpStatusCode.BadRequest, ErrorLookup.GetErrorMessage("invalid_input"), Configuration.Formatters.JsonFormatter);
var userId = User.Identity.GetUserId();
if (userId == null)
return Request.CreateResponse(HttpStatusCode.BadRequest, ErrorLookup.GetErrorMessage("invalid_token"), Configuration.Formatters.JsonFormatter);
var accUser = await GetUserById(userId);
_currentUser = AccountUtils.GetOrgAndUserInfo(accUser);
var callbackUrl = $"{Request.RequestUri.GetLeftPart(UriPartial.Authority)}/oauth2/callback";
IOAuthHandler handler;
switch (request.Provider)
{
case "Fitbit":
handler = new FitbitHandler(callbackUrl);
break;
case "Withings":
handler = new WithingsHandler(callbackUrl);
break;
default:
return Request.CreateResponse(HttpStatusCode.BadRequest, ErrorLookup.GetErrorMessage("invalid_input"), Configuration.Formatters.JsonFormatter);
}
var authorizationUrl = handler.RequestUserAuthorizationUrl(request.PatientID,_currentUser.Org);
return Request.CreateResponse(HttpStatusCode.OK, authorizationUrl);
}
[HttpPost]
public async Task<HttpResponseMessage> Callback(UserAuthDTO request)
{
if (string.IsNullOrEmpty(request.PatientID))
Request.CreateResponse(HttpStatusCode.BadRequest, ErrorLookup.GetErrorMessage("invalid_input"), Configuration.Formatters.JsonFormatter);
var userId = User.Identity.GetUserId();
if (userId == null)
You are setting the callback url to
...oauth2/callback?code=3aa6e9e....
But in your action, your route is
...api/oauth2/callback
You are missing "api" in your definition

return errors from web api 2 account register

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.

Categories