webapi parameter is null - c#

I am starting WebApi tutorial but I just faced a problem that parameter in action is a null.
Below is the tutorial code.
WebApi
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var raw = Request.Content.ReadAsStringAsync();
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, Result.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
WebApi Client
static string Register(string email, string password)
{
var Result = new RegisterBindingModel()
{
Email = email,
Password = password,
ConfirmPassword = password
};
using (var client = new HttpClient())
{
var response = client.PostAsJsonAsync(
"http://localhost:7399/api/Account/Register",
Result).Result;
return response.StatusCode.ToString();
}
}
Register action receives http request but model is always null. So the raw variable shows like this
{"Email":"test#gmail.com","Password":"Test#123","ConfirmPassword":"Test#123"}
But when I tried sending http request using Postman it worked. As request body was read for model binding. The raw variable was empty. I don't know what's wrong with my client. I followed exactly tutorial code. Should I specify content-type?

make variable name same i.e. in Register method change var Result to var model and have a try.
it should be frombody , i.e. get parameter value from post body
[HttpPost]
public async Task<IHttpActionResult> Register([FromBody]RegisterBindingModel model)

Add HttpPost And Use below Methods
[AllowAnonymous]
[HttpPost]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
//your code
}
Use this Methods:-
public static async Task<HttpResponseMessage> PostAsJsonAsync(this HttpClient client,
string requestUri, object requestObject, Dictionary<string, string> requestHeaders = null,
int? timeoutMilliSeconds = null)
{
var jsonContent = JsonConvert.SerializeObject(requestObject);
var requestContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
return await SendAsync(client, requestUri, HttpMethod.Post,
requestContent, requestHeaders, timeoutMilliSeconds);
}
public static async Task<HttpResponseMessage> SendAsync(
this HttpClient client,
string requestUri, HttpMethod httpMethod, HttpContent requestContent = null,
Dictionary<string, string> requestHeaders = null, int? timeoutMilliSeconds = null)
{
var httpRequestMessage = new HttpRequestMessage
{
RequestUri = new Uri(requestUri),
Method = httpMethod,
Content = requestContent
};
if (requestHeaders != null)
{
foreach (var requestHeader in requestHeaders)
{
httpRequestMessage.Headers.Add(requestHeader.Key, requestHeader.Value);
}
}
if (timeoutMilliSeconds.HasValue)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(new TimeSpan(0, 0, 0, 0, timeoutMilliSeconds.Value));
return await client.SendAsync(httpRequestMessage, cts.Token);
}
else
{
return await client.SendAsync(httpRequestMessage);
}
}

Related

How to return an html document from WebApi 2 action using IHttpActionResult interface?

I need to build an html document and return it in my web api. All available answers on the net and the forum suggest using HttpResponseMessage. I would like to achieve this by IHttpActionResult. Below is what I have thus far:
[ResponseType(typeof(HttpResponseMessage))]
public async Task<IHttpActionResult> GetNotesViewModels()
{
var note = await GetHtmlText();
var response = new HttpResponseMessage();
response.Content = new StringContent(note);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return Ok(ResponseMessage(response));
}
I am not receiving what I would like. What is missing here?
You could implement your own HtmlResult, like following (free-handed):
public class HtmlActionResult : IHttpActionResult
{
public HtmlActionResult (HttpRequestMessage request, string content)
{
Request = request;
Content= content;
}
public string Content { get; private set; }
public HttpRequestMessage Request { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(ExecuteResult());
}
public HttpResponseMessage ExecuteResult()
{
var response = new HttpResponseMessage();
if (!string.IsNullOrWhiteSpace(Content))
response.Content = new StringContent(Content, Encoding.UTF8, "text/html");
response.RequestMessage = Request;
return response;
}
}
And use it like this:
public async Task<IHttpActionResult> GetNotesViewModels()
{
var note = await GetHtmlText();
return new HtmlActionResult(Request, note);
}

Mock returns null value when ReturnResult is a custom object but works as expected when it is a primitive type bool

I am using Moq in .net core(1.1) and having a bit of torrid time understanding this behavior as all the examples on interweb points to the fact the this should work with no issues.
I have already tried with:
Returns(Task.FromResult(...)
Returns(Task.FromResult(...)
ReturnsAsync(...)
Mocking a IHttpClient interface to wrap PostAsync, PutAsync and GetAsync. All of these return an ApiResponse object.
var mockClient = new Mock<IHttpClient>();
Does not work:
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null))
.Returns(Task.FromResult(new ApiResponse()));
PostSync definition:
public async Task<ApiResponse> PostAsync(string url, string body, string authToken = null)
Does work:
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null))
.Returns(Task.FromResult(bool));
PostSync definition:
public async Task<bool> PostAsync(string url, string body, string authToken = null)
Usage:
var api = new ApiService(mockClient.Object);
var response = api.LoginAsync(body.Username, body.Password);
UPDATE
[Fact]
public async void TestLogin()
{
var mockClient = new Mock<IHttpClient>();
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null)).Returns(Task.FromResult(new ApiResponse()));
var api = new ApiService(mockClient.Object);
var response = await api.LoginAsync(body.Username, body.Password);
Assert.IsTrue(response);
}
Return Type:
public class ApiResponse
{
public string Content { get; set; }
public HttpStatusCode StatusCode { get; set; }
public string Reason { get; set; }
}
LoginAsync:
public async Task<bool> LoginAsync(string user, string password)
{
var body = new { Username = user, Password = password };
try
{
var response = await _http.PostAsync(_login_url, JsonConvert.SerializeObject(body), null);
return response .State == 1;
}
catch (Exception ex)
{
Logger.Error(ex);
return false;
}
}
PostAsync:
public async Task<object> PostAsync(string url, string body, string authToken = null)
{
var client = new HttpClient();
var content = new StringContent(body, Encoding.UTF8, "application/json");
var response = await client.PostAsync(new Uri(url), content);
var resp = await response.Result.Content.ReadAsStringAsync();
return new ApiResponse
{
Content = resp,
StatusCode = response.Result.StatusCode,
Reason = response.Result.ReasonPhrase
};
}
Assuming a simple method under test like this based on minimal example provided above.
public class ApiService {
private IHttpClient _http;
private string _login_url;
public ApiService(IHttpClient httpClient) {
this._http = httpClient;
}
public async Task<bool> LoginAsync(string user, string password) {
var body = new { Username = user, Password = password };
try {
var response = await _http.PostAsync(_login_url, JsonConvert.SerializeObject(body), null);
return response.StatusCode == HttpStatusCode.OK;
} catch (Exception ex) {
//Logger.Error(ex);
return false;
}
}
}
The following test works when configured correctly
[Fact]
public async Task Login_Should_Return_True() { //<-- note the Task and not void
//Arrange
var mockClient = new Mock<IHttpClient>();
mockClient
.Setup(x => x.PostAsync(It.IsAny<string>(), It.IsAny<string>(), null))
.ReturnsAsync(new ApiResponse() { StatusCode = HttpStatusCode.OK });
var api = new ApiService(mockClient.Object);
//Act
var response = await api.LoginAsync("", "");
//Assert
Assert.IsTrue(response);
}
The above is just for demonstrative purposes only to show that it can work provided the test is configured properly and exercised based on the expected behavior.
Take some time and review the Moq quick start to get a better understanding of how to use the framework.

WebAPI: using async methods in business logic

The aim is to make controller that uses async method in my custom service.
Controller:
[Route("api/data/summary")]
[HttpGet]
public async Task<IHttpActionResult> Get()
{
var result = await DataService.GetDataObjects();
return Ok(result);
}
Service:
public static async Task<IEnumerable<DataObject>> GetDataObjects()
{
var apiKey = "some-api-key";
var path = "path-to-external-service";
using (var client = new HttpClient())
{
var dataToProcess = // some data object
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.BaseAddress = new Uri(path);
HttpResponseMessage response = await client.PostAsJsonAsync("", dataToProcess);
var content = await response.Content.ReadAsStringAsync();
var result = MakeEntities(content); // some logic
return result;
}
}
But I came across with the problem that controller's action returns empty result before service actually finished processing data.
Could you please advice how to implement it correctly?
Your code is OK and controller doesn't seem to return a value before GetDataObjects returns value.
Except for the situations below:
MakeEntities uses some asynchronous operation and you don't await it inside MakeEntities. So MakeEntities return task.
Exception rises while your code is running. Make sure GetDataObjects and MakeEntities code works fine.
The aim is to make controller that uses async method in my custom service.
Controller:
[HttpGet]
[Route("api/data/summary")]
public async Task<IHttpActionResult> Get()
{
var result = await DataService.GetDataObjects().ConfigureAwait(false);
return Ok(result);
}
Service:
public static async Task<ResponseEntity> GetDataObjects()
{
ResponseEntity response = new ResponseEntity();
var apiKey = "some-api-key";
var path = "path-to-external-service";
using (var client = new HttpClient())
{
var dataToProcess = // some data object
client.BaseAddress = new Uri(path);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync("", dataToProcess).ConfigureAwait(false);
string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var result = JsonConvert.DeserializeObject<ResponseEntity>(responseString);
return response;
}
}

HttpContext.Current is null during test

I have a .NET C# Web API application. I have a single url controller endpoint which receives a POST message. When I run the app and I use an external tool to send the POST message it works perfectly fine.
However, when I trigger the controller from my unit test I get a null ref. exception because for some reason HttpContext.Current is null
This is my current controller (which works in a real scenario):
[HttpPost]
public async Task<IHttpActionResult> Post()
{
await Request.Content.ReadAsStringAsync();
if (Request.Content.IsFormData())
{
var stuff = HttpContext.Current.Request["stuff"];
}
return Ok();
}
}
This is my unit test file:
[TestFixture]
public class AnnotationsControllerTest : BaseIntegrationTest
{
private const string Uri = "http://localhost:2622/api/annotations";
[Test]
public async void TestHistoriesPost()
{
var form = new List<KeyValuePair<string, string>>();
form.Add(new KeyValuePair<string, string>("stuff", "123456"));
using (var request = new HttpRequestMessage(HttpMethod.Post, Uri))
using (var config = new HttpConfiguration())
{
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
using (var content = new FormUrlEncodedContent(form))
{
request.Content = content;
var mockDataService = GetDataServices();
var controller = new AnnotationsController(mockDataService.Object, ApiTestConfiguration());
SetupController(route, controller, config, request);
var actionResult = await controller.Post();
var httpResponseMessage = await actionResult.ExecuteAsync(CancellationToken.None);
Assert.AreEqual(HttpStatusCode.OK, httpResponseMessage.StatusCode);
}
}
}
private static void SetupController(
IHttpRoute route,
ApiController controller,
HttpConfiguration configuration,
HttpRequestMessage request)
{
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "Annotations" } });
controller.ControllerContext = new HttpControllerContext(configuration, routeData, request);
configuration.Services.Replace(typeof(IExceptionHandler), new UnhandledExceptionHandler());
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = configuration;
}
private Mock<IDataServices> GetDataServices()
{
return new Mock<IDataServices>();
}
}

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