C# From ObjectResult to string (or get value) - c#

I'm using a IActionResult (task) to upload a file and i refference that in my controller. What i want to get back is the file name.
Controller ->
var imageLocation = await _imageHandler.UploadImage(image);
ImageHandler ->
public async Task<IActionResult> UploadImage(IFormFile file)
{
var result = await _imageWriter.UploadImage(file);
return new ObjectResult(result);
}
My value is stored in imageLocation, but i have no idea how to access it (i need the "Value" string so i can add it to DB).
I've tried searching for everything, but everyone is using a list. I only need a string here.
Hopefully you guys can help me out. Thanks!

You can cast the result to the desired type and call property
Controller
var imageLocation = await _imageHandler.UploadImage(image);
var objectResult = imageLocation as ObjectResult;
var value = objectReult.Value;
or just refactor the ImageHandler.UploadImage function to return the actual type to avoid having to cast
public async Task<ObjectResult> UploadImage(IFormFile file) {
var result = await _imageWriter.UploadImage(file);
return new ObjectResult(result);
}
and get the value as expected in controller
var imageLocation = await _imageHandler.UploadImage(image);
var value = imageLocation.Value;
Better yet, have the function just return the desired value
public Task<string> UploadImage(IFormFile file) {
return _imageWriter.UploadImage(file);
}
that way you get what is expected when calling the function in the controller.
string imageLocation = await _imageHandler.UploadImage(image);

Just came across a Result.Value from FluentValidation Errors:
ObjectResult? result = (await controller.CreateSomething(InvalidDTO)) as ObjectResult;
List<FluentValidation.Results.ValidationFailure> value = (List<FluentValidation.Results.ValidationFailure>)result.Value;

Related

Get JsonResult within ActionResult

My data is being generated at a JsonResult which output is below.
public JsonResult SampleAAA(Guid xxxxx){
//Code ...
return Json(new
{
success = true,
listA,
listB,
}, JsonRequestBehavior.AllowGet);
}
From an ActionResult I need to call that JsonResult and get those lists.
public ActionResult SampleBBB(Guid xxxxx){
var result = SampleAAA(xxxx);
//How can I access 'listA' and 'listB' ?
//Other code ...
Return View();
}
How can I access those lists? I do see result.Data content, but I can't reach those lists to continue my code at Visual Studio.
you can try this code
var result = SampleAAA(xxxx);
var jsonObject = (JObject)result.Value;
bool success = (bool)jsonObject["success"];
and it's much better to use JsonResult
return new JsonResult(new
{
success = true,
listA,
listB,
});
Using .Net 5+ you can do this:
using System.Text.Json;
var doc = JsonDocument.Parse(YOUR_JSON);
var property = doc.RootElement.GetProperty("ListA");
Thanks for all replies. After more research I've managed to solve with the code below.
public ActionResult SampleBBB(Guid xxxxx){
var result = SampleAAA(xxxx);
//Steps to have access to those information:
string stringJson = JsonSerializer.Serialize(result.Data);
var resultString = JObject.Parse(stringJson);
var outputA = resultString["listA"].Value<string>();
//...
}

How to test a controller POST method which returns no data in response content in .NET Core 3.1?

i am new to integration tests. I have a controller method which adds a user to the database, as shown below:
[HttpPost]
public async Task<IActionResult> CreateUserAsync([FromBody] CreateUserRequest request)
{
try
{
var command = new CreateUserCommand
{
Login = request.Login,
Password = request.Password,
FirstName = request.FirstName,
LastName = request.LastName,
MailAddress = request.MailAddress,
TokenOwnerInformation = User
};
await CommandBus.SendAsync(command);
return Ok();
}
catch (Exception e)
{
await HandleExceptionAsync(e);
return StatusCode(StatusCodes.Status500InternalServerError,
new {e.Message});
}
}
As you have noticed my method returns no information about the user which has been added to the database - it informs about the results of handling a certain request using the status codes. I have written an integration test to check is it working properly:
[Fact]
public async Task ShouldCreateUser()
{
// Arrange
var createUserRequest = new CreateUserRequest
{
Login = "testowyLogin",
Password = "testoweHaslo",
FirstName = "Aleksander",
LastName = "Kowalski",
MailAddress = "akowalski#onet.poczta.pl"
};
var serializedCreateUserRequest = SerializeObject(createUserRequest);
// Act
var response = await HttpClient.PostAsync(ApiRoutes.CreateUserAsyncRoute,
serializedCreateUserRequest);
// Assert
response
.StatusCode
.Should()
.Be(HttpStatusCode.OK);
}
I am not sure is it enough to assert just a status code of response returned from the server. I am confused because, i don't know, shall i attach to assert section code, which would get all the users and check does it contain created user for example. I don't even have any id of such a user because my application finds a new id for the user while adding him/her to the database. I also have no idea how to test methods like that:
[HttpGet("{userId:int}")]
public async Task<IActionResult> GetUserAsync([FromRoute] int userId)
{
try
{
var query = new GetUserQuery
{
UserId = userId,
TokenOwnerInformation = User
};
var user = await QueryBus
.SendAsync<GetUserQuery, UserDto>(query);
var result = user is null
? (IActionResult) NotFound(new
{
Message = (string) _stringLocalizer[UserConstants.UserNotFoundMessageKey]
})
: Ok(user);
return result;
}
catch (Exception e)
{
await HandleExceptionAsync(e);
return StatusCode(StatusCodes.Status500InternalServerError,
new {e.Message});
}
}
I believe i should somehow create a user firstly in Arrange section, get it's id and then use it in Act section with the GetUserAsync method called with the request sent by HttpClient. Again the same problem - no information about user is returned, after creation (by the way - it is not returned, because of my CQRS design in whole application - commands return no information). Could you please explain me how to write such a tests properly? Have i missed anything? Thanks for any help.
This is how I do it:
var response = (CreatedResult) await _controller.Post(createUserRequest);
response.StatusCode.Should().Be(StatusCodes.Status201Created);
The second line above is not necessary, just there for illustration.
Also, your response it's better when you return a 201 (Created) instead of the 200(OK) on Post verbs, like:
return Created($"api/users/{user.id}", user);
To test NotFound's:
var result = (NotFoundObjectResult) await _controller.Get(id);
result.StatusCode.Should().Be(StatusCodes.Status404NotFound);
The NotFoundObjectResult assumes you are returning something. If you are just responding with a 404 and no explanation, replace NotFoundObjectResult with a NotFoundResult.
And finally InternalServerErrors:
var result = (ObjectResult) await _controller.Get(id);
result.StatusCode.Should().Be(StatusCodes.Status500InternalServerError);
You can use integrationFixture for that using this NuGet package. This is an AutoFixture alternative for integration tests.
The documented examples use Get calls but you can do other calls too. Logically, you should test for the status code (OkObjectResult means 200) value and the response (which could be an empty string, that is no problem at all).
Here is the documented example for a normal Get call.
[Fact]
public async Task GetTest()
{
// arrange
using (var fixture = new Fixture<Startup>())
{
using (var mockServer = fixture.FreezeServer("Google"))
{
SetupStableServer(mockServer, "Response");
var controller = fixture.Create<SearchEngineController>();
// act
var response = await controller.GetNumberOfCharacters("Hoi");
// assert
var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
Assert.Contains("Hoi", request.RawQuery);
Assert.Equal(8, ((OkObjectResult)response.Result).Value);
}
}
}
private void SetupStableServer(FluentMockServer fluentMockServer, string response)
{
fluentMockServer.Given(Request.Create().UsingGet())
.RespondWith(Response.Create().WithBody(response, encoding: Encoding.UTF8)
.WithStatusCode(HttpStatusCode.OK));
}
In the example above, the controller is resolved using the DI described in your Startup class.
You can also do an actual REST call using using Refit. The application is self hosted inside your test.
using (var fixture = new RefitFixture<Startup, ISearchEngine>(RestService.For<ISearchEngine>))
{
using (var mockServer = fixture.FreezeServer("Google"))
{
SetupStableServer(mockServer, "Response");
var refitClient = fixture.GetRefitClient();
var response = await refitClient.GetNumberOfCharacters("Hoi");
await response.EnsureSuccessStatusCodeAsync();
var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
Assert.Contains("Hoi", request.RawQuery);
}
}

Dealing with TempData variables in Unit Tests

Following the example from here:
Mocking a TempData in ASP.NET Core in MSTest,
I wrote down the following TestMethod:
[Fact]
public void TestBackMethod()
{
var httpContext = new DefaultHttpContext();
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
tempData["id"] = 3008;
var controller = new PhaseController(Configuration)
{
TempData = tempData
};
var result = controller.Back() as ViewResult;
Assert.Contains("Index", result.ViewName);
}
For this Controller Method:
public IActionResult Back()
{
int releaseId = (int)TempData["id"];
return RedirectToAction("Index", "Phase", new { id = releaseId });
}
However, on this line:
Assert.Contains("Index", result.ViewName);
result is null.
'Object reference not set to an instance of an object.'
Why is this happening and how can I fix it?
Because Back doesn't return a ViewResult - it returns a RedirectToActionResult (you can hover over RedirectToAction to see the exact object name). Both of these implement IActionResult.
You get a Null Reference Exception because when you use the as keyword for casting objects it will return null if the conversion is not possible.
If you instead had
var result = (ViewResult)controller.Back();
You would get a difference exception during run time of that line saying the conversion was not possible.
You should do the conversion doing one of these methods:
var result = (RedirectToActionResult)controller.Back();
or
var result = controller.Back() as RedirectToActionResult;

How can I upload a file and form data using Flurl?

I'm trying to upload a file with body content. Is PostMultipartAsync the only way?
On my C# backend code I have this:
var resource = FormBind<StorageFileResource>();
var file = Request.Files.First().ToPostedFile();
FormBind reads data from the request and fills the object.
By using PostMultipartAsync I know it should start like this:
.PostMultipartAsync((mp) => { mp.AddFile(name, stream, name)}), but I can't figure out how to add the object. Do you have any ideas on that?
This is my current try:
public static async Task<T> PostFileAsync<T>(string url, object data, string name, Stream stream, object queryString = null)
where T : class
{
return await HandleRequest(async () => queryString != null
? await url
.SetQueryParams(queryString)
.SetClaimsToken()
.PostMultipartAsync((mp) => { mp.AddFile(name, stream, name)})
.ReceiveJson<T>()
: await url
.SetClaimsToken()
.PostMultipartAsync((mp) => mp.AddFile(name, stream, name))
.ReceiveJson<T>());
}
Current request being made by the front end:
There are a variety of ways to add "parts" to a multipart POST with Flurl. I haven't added this to the docs yet but here's an example from the issue that basically demonstrates every possibility:
var resp = await "http://api.com"
.PostMultipartAsync(mp => mp
.AddString("name", "hello!") // individual string
.AddStringParts(new {a = 1, b = 2}) // multiple strings
.AddFile("file1", path1) // local file path
.AddFile("file2", stream, "foo.txt") // file stream
.AddJson("json", new { foo = "x" }) // json
.AddUrlEncoded("urlEnc", new { bar = "y" }) // URL-encoded
.Add(content)); // any HttpContent
Here is one way that works for me
var result = await endPointApi
.AppendPathSegments("api","AppFileManager")
.WithOAuthBearerToken(token.AccessToken)
.PostMultipartAsync(mp => mp
//.AddFile("UploadFile", #"C:\Users\..\Documents\upload.txt")
.AddFile("UploadFile", new MemoryStream(data), appFile.FileName)
.AddStringParts(new
{
IRN = appFile.IRN,
TransactionIRN = appFile.TransactionIRN,
FileName = appFile.FileName,
TableName = appFile.TableName,
FileExtension = appFile.FileExtension,
})
Web Api Controller Implementation (using MediatR)
[HttpPost]
public async Task<IActionResult> Post([FromForm] AppFileManagerCommands.Upload uploadAttachment)
{
await mediator.Send(uploadAttachment);
return NoContent();
}

Assert for empty content in 200 Http Response

I have a simple Web Api method which returns a list. I decided as a general project rule, that if the list is empty for a particular userId, we return an Ok() method with empty content.
My web api method looks like following:
[Route("")]
[HttpGet]
public IHttpActionResult GetPersonalList()
{
var result = _facade.Get(_userContext.Get());
if (result == null)
return Ok(); //here is the point
return Ok(new PersonalExpensesReportViewModel(result));
}
Trying to make a 100% of coverage of this method, I wanted to test the scenario that I mentioned, but I could not achieve how to write the assert for the empty content.
[TestMethod]
public void GetPersonalList_NoContent_Ok()
{
//Arrange
_facade.Setup(x => x.Get(_userContext.Object.GetPersonnelNumber(), null)).Returns((PersonalExpensesReport)null);
//Act
var result = _controller.GetPersonalList();
//Assert
var negociatedResult = result as OkResult;
Assert.IsNotNull(result);
// ?? I want something like Assert.IsNull(negociatedResult.Content)
}
Being that I don't have a certain type to make result as OkNegotiatedContentResult which expects T type to be instantiated, I thought about cast as OkResult, but I don't have the 'Content' property in this class.
Does someone know how to proceed in this cases?
Please try to use OkNegotiatedContentResult<T> like:
var result = _controller.GetPersonalList();
var response = result as OkNegotiatedContentResult<PersonalExpensesReportViewModel>;
Assert.IsNotNull(response);
var content = response.Content;
Assert.AreEqual(5, content.Count());
[TestMethod]
public void GetPersonalList_NoContent_Ok()
{
var serviceresponse = new yourresponseobject<yourmodel>{
Message = "what ever response";
Data = null;
};
var service = new Mock<youserviceInterface>(MockBehavior.Strict);
service.Setup(x => x.GetPersonalList()(It.IsAny<string>())).ReturnsAsync(serviceResponse); /// for number of parameters in controller method, add It.IsAny<string>()
//Arrange
_facade.Setup(x => x.Get(_userContext.Object.GetPersonnelNumber(), null)).Returns((PersonalExpensesReport)null);
//Act
var result = _controller.GetPersonalList();
//Assert
var negociatedResult = result as Object;
Assert.IsNotNull(result.value);
Assert.AreEqual(200,result.negociatedResult.statuscode);
}

Categories

Resources