Is there any in-built function/method to return, IActionResult/ActionResult instead of HttpResponseMessage in .Net Core 3.1 - asp.net-core

My Action method is returning HttpResponseMessage but, I want to get rid off Microsoft.AspNetCore.Mvc.WebApiCompatShim NuGet Package (which is basically provided to bridge the gap while porting Asp.Net Web API code into .Net Core) and use IActionResult/ActionResult instead of HttpResponseMessage.
My Action method looks like this:
[HttpGet]
[Route("GetTemplate")]
public async Task<HttpResponseMessage> GetTemplate(string id) {
var userAgent = this.Request.Headers.UserAgent;
bool IsWindows = true;
if(userAgent.ToString().ToLower().Contains("apple")) {
IsWindows = false; //false
}
var template = await _templateService.GetTemplateContent(id);
HttpResponseMessage responseMsg = new HttpResponseMessage();
if(IsWindows) {
responseMsg.Content = new StringContent(JsonConvert.SerializeObject(template));
responseMsg.RequestMessage = Request;
responseMsg.StatusCode = HttpStatusCode.OK;
responseMsg.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/json");
} else {
responseMsg.Content = new ByteArrayContent(template.ContentBytes);
responseMsg.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileNameStar = template.Name };
responseMsg.Content.Headers.Add("x-filename", template.Name);
responseMsg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
responseMsg.Content.Headers.ContentLength = template.ContentBytes.Length;
responseMsg.RequestMessage = Request;
responseMsg.StatusCode = HttpStatusCode.OK;
}
return (responseMsg);
}

Since you aren’t doing anything fancy there, you can translate your return object directly into corresponding action results here. In your case, you want a JsonResult and a FileResult with a custom response header:
[HttpGet]
[Route("GetTemplate")]
public async Task<HttpResponseMessage> GetTemplate(string id)
{
var userAgent = this.Request.Headers.UserAgent;
bool IsWindows = !userAgent.ToString().ToLower().Contains("apple");
var template = await _templateService.GetTemplateContent(id);
if (IsWindows)
{
return Json(template);
}
else
{
Response.Headers.Add("x-filename", template.Name);
return File(template.ContentBytes, "application/octet-stream", template.Name);
}
}
There are a lot similar utility methods on the Controller and ControllerBase type that help you create a variety of different response messages. For most use cases, there should be a built-in way to produce the response.

1stly change the signature of your action to this:
public async Task<IActionResult> GetTemplate
You will then return your data in the response something like this return Ok(data). You do not have to serialize your data, you can send a POCO class. This would represent .StatusCode = HttpStatusCode.OK
If you want to add extra headers to your response, you will do so using the Response field from ControllerBase. Eg. Response.Headers.Add for adding key value pairs to your Response header.

Related

Error in ASP.NET Core MVC and Web API project

I have an ASP.NET Core MVC and also Web API project.
This error occurs when I try to send project information to the API (of course API works fine and I do not think there is a problem):
UnsupportedMediaTypeException: No MediaTypeFormatter is available to read a "TokenModel" object of "text / plain" media content.
My code is:
public class TokenModel
{
public string Token { get; set; }
}
and in AuthController I have:
var _Client = _httpClientFactory.CreateClient("MyApiClient");
var jsonBody = JsonConvert.SerializeObject(login);
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = _Client.PostAsync("/Api/Authentication", content).Result;
if (response.IsSuccessStatusCode)
{
var token = response.Content.ReadAsAsync<TokenModel>().Result;
}
The error occurs on this line:
var token = response.Content.ReadAsAsync<TokenModel>().Result;
HomeController:
public IActionResult Index()
{
var token = User.FindFirst("AccessToken").Value;
return View(_user.GetAllUsers(token));
}
UserRepository:
public List<UserViewModel> GetAllUsers(string token)
{
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var res = _client.GetStringAsync(UrlMyApi).Result;
List<UserViewModel> users = JsonConvert.DeserializeObject<List<UserViewModel>>(res);
return users;
}
Your API is returning content-type of text/plain and none of the default media type formatters(MediaTypeFormatter) which ReadAsAsync<string>() will try to use support parsing it as is. They work with JSON/XML. You can go a couple of ways but maybe the easiest is to read the content as string and deserialize it after:
var tokenJSON = response.Content.ReadAsStringAsync().Result;
var token = JsonConvert.DeserializeObject<TokenModel>(tokenJSON);
Also, as you're using the Async methods, you should be returning Task from your actions and await the result instead of using .Result as you're just creating overhead currently.
var tokenJSON = await response.Content.ReadAsStringAsync();
var token = JsonConvert.DeserializeObject<TokenModel>(tokenJSON);

ASP.NET Core 3.1 - PostAsync/PostAsJsonAsync method in Integration Test always returns Bad Request

This is my register method inside the AuthController.
[HttpPost(ApiRoutes.Auth.Register)]
public async Task<IActionResult> Register(UserRegistrationRequest request)
{
var authResponse = await _authService.RegisterAsync(request.Email, request.Password);
if (!authResponse.Success)
{
return BadRequest(new AuthFailedResponse
{
Errors = authResponse.Errors
});
}
return Ok(new AuthSuccessResponse
{
Token = authResponse.Token,
RefreshToken = authResponse.RefreshToken
});
}
I'm trying to call this method by using TestClient.PostAsync() method, unfortunately it always returns Bad Request. I've already tried calling the TestClient.PostAsJsonAsync(ApiRoutes.Auth.Register, user) method by importing Microsoft.AspNet.WebApi.Client package, the result is the same.
var user = new UserRegistrationRequest
{
Email = "user1#testtest.com",
Password = "P#ssw0rd1!!!!!"
};
var response = await TestClient.PostAsync(
ApiRoutes.Auth.Register,
new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8)
{
Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
});
You are missing the FromBody attribute from you action parameter. When you are sending json data to a controller that will be part of the request body. You can tell to the controller how to bind the incoming data, in your case from the body. So you code should look like:
public async Task<IActionResult> Register([FromBody]UserRegistrationRequest request)
{
…
}
You could read more about bindings in the official documentation.

Asp.net Core 2.1 HttpClientFactory: second api call is not waiting for first api call's returned result

I encountered an issue using HttpClientFactory. I need to call two web methods from one third party web api.
getOrderNumber.
getShippingLabelFile.
Call #2 depends on #1's result since it needs to pass orderNumber to it e.g.:
await _client.getAsync("http://xxx/api/getLabel?orderNumber=[returnedOrderNumber]&fileType=1")
When I set break-point and debug, it works as expected. Without debugging mode, #2 web method always failed. I have done investigation. If I pass static query parameter like:
http://xxx/api/getLabel?orderNumber=123&fileType=1
it works fine. It seems #2 evaluates the query string and execute api call before orderNumber gives to it. It is very frustrating, can you please shed on some light on this issue?
On Controller:
private readonly ISite1AuthHttpClient _site1HttpClient;
public OrderShippingOrdersController(site1AuthHttpClient)
{
_site1HttpClient=site1AuthHttpClient
}
[HttpGet("{id}")]
public async Task<IActionResult> GetShippingLabel(int id)
{
string token=await _site1HttpClient.GetToken(username.ToString(),password);
string orderNumber=await _site1HttpClient.CreateOrder(Order,token);
if (orderNumber!=null && orderNumber!="")
{
//this API call always failed during runtime. It works on debugging mode.
var streamFile=(MemoryStream)(await _site1HttpClient.getShippingLabel(orderNumber,token));
}
}
HttpClient Type Class:
public interface ISite1HttpClient
{
Task<string> CreateOrder(AueCreateOrder order,string token);
Task<Stream> GetShippingLabel(string orderNumber,string token);
}
public class Site1HttpClient:ISite1HttpClient
{
private readonly HttpClient _client;
public Site1HttpClient(HttpClient httpClient)
{
httpClient.BaseAddress = new Uri("http://abcapi.Site1.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
_client = httpClient;
}
public async Task<string> CreateOrder(AbcCreateOrder order,string token)
{
var jsonInString=JsonConvert.SerializeObject(order);
jsonInString="[ " + jsonInString + " ]";
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
HttpResponseMessage response = await _client.PostAsync(
"api/AgentShipmentOrder/Create", new StringContent(jsonInString, Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
var contents = await response.Content.ReadAsStringAsync();
AbcOrderCreateResponse abcRes = JsonConvert.DeserializeObject<AbcOrderCreateResponse>(contents);
return abcRes.Message;
}
else
{
var errorResponse = await response.Content.ReadAsStringAsync();
throw new Exception(errorResponse);
}
}
public async Task<Stream> GetShippingLabel(string orderNumber,string token)
{
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
HttpResponseMessage response = await _client.GetAsync("api/GetOrderLabel?orderId="+orderNumber+"&fileType=1");
if (response.IsSuccessStatusCode)
{
Stream streamFile= await response.Content.ReadAsStreamAsync();
return streamFile;
}
else
{
throw new Exception("failed to get label.");
}
}
}
string token = _site1HttpClient.GetToken(username.ToString(),password);
string orderNumber = await _site1HttpClient.CreateOrder(Order,token);
I guess the problem occurs because of first await keyword. When you use await for the first function call (calling an async function), you declare that your program does not need to hold on for the response. So the token variable is used in the second function when it is not set. As you can see above, you should be good to go without the first await for the token variable.

How do I get the model state from a HTTPRequestResponse from within a console application

I have an APS.NET Core 2.0 API that I am writing a test client for. The test client is a console application. I want to be able to read and display any errors returned from may API call that would be in the model state.
In my API, if the model is not valid, I return the model along with a status 422 as follows;
if (!ModelState.IsValid)
{
return new UnprocessableEntityObjectResult(ModelState);
}
The UnprocessableEntityObjectResult is just a helper class, as shown below;
public class UnprocessableEntityObjectResult : ObjectResult
{
public UnprocessableEntityObjectResult(ModelStateDictionary modelState)
: base(new SerializableError(modelState))
{
if (modelState == null)
{
throw new ArgumentNullException(nameof(modelState));
}
StatusCode = 422;
}
}
My intent is to return the modelstate to the client, on error.
My test client is a console application and I am looking for a way to examine the model state and list out any errors.
In my console application, I have the following method that is called from Main;
static async Task CreateUploadRecordAsync()
{
httpClient.BaseAddress = new Uri("https://localhost:44369");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string relativeUrl = "/api/upload";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, relativeUrl);
HttpResponseMessage response = new HttpResponseMessage();
using (var content = new MultipartFormDataContent())
{
//Add content values here...
request.Content = content;
response = await httpClient.SendAsync(request);
}
if (response.IsSuccessStatusCode)
{
string result = response.Headers.Location.ToString();
Console.WriteLine("Success:\n");
Console.WriteLine($"New Record Link: [{result}]\n");
}
else
{
//Add code here to get model state from response
Console.WriteLine($"Failed to create new upload record. Error: {response.ReasonPhrase}\n");
}
}
I am looking for an example of how to extract the model state that would exist after the "/Add code here to get model state from response" comment.
Any ideas?

MVC aynchronous method

I am working on a MVC project that submits a request via a third party.
In my controller, I have a SubmitClaims() action that receive ajax request and then calls RunAsync(). RunAsync submits a request by using HttpClient.
I am not sure if I did a right thing here.
Also I have two version of SubmitClaims(), both work. But I don't know which version is better.
version 1
[HttpPost]
public async Task<string> SubmitClaims()
{
string result = "";
result = await RunAsync();
return result;
}
version 2 learn from Cannot implicitly convert type 'string' to 'System.Threading.Tasks.Task<string>'
[HttpPost]
public async Task<string> SubmitClaims()
{
return await Task.Run(() =>
{
return RunAsync();
});
}
static async Task<string> RunAsync()
{
string result = "Failed.";
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("http://peter:8001/internal/uickpost");
client.DefaultRequestHeaders.Add("contenttype", "application/xml");
client.DefaultRequestHeaders.Add("hiconline.protocol.content.role", "REQUEST");
client.DefaultRequestHeaders.Add("hiconline.protocol.content.transactionid", "asdfsdf");
client.DefaultRequestHeaders.Add("hiconline.protocol.remote.contenttype", "TestDataType");
client.DefaultRequestHeaders.Add("hiconline.protocol.remote.mode", "P");
client.DefaultRequestHeaders.Host = "peter:8001";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
string opv = "Test Data";
HttpContent _content = new StringContent(opv);
_content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
_content.Headers.Add("contenttype", "TestDataType");
HttpResponseMessage response1 = await client.PostAsync(client.BaseAddress, _content);
if (response1.IsSuccessStatusCode)
{
Uri gizmoUrl = response1.Headers.Location;
result = response1.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
result = ex.Message;
}
return result;
}
}
Option 1 is better. RunAsync() already returns a task, so why create another one?
Even better would be return await RunAsync();. Even better would just be calling RunAsync directly, since the wrapper doesn't add anything.