Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REFIT: Upload file or image in one object #107

Closed
Strypper opened this issue Feb 23, 2023 · 2 comments · Fixed by #110
Closed

REFIT: Upload file or image in one object #107

Strypper opened this issue Feb 23, 2023 · 2 comments · Fixed by #110
Assignees
Labels
enhancement 🔥 New feature or request level/extreme 🥵 new feature 🚀 New feature addition
Milestone

Comments

@Strypper
Copy link
Owner

Strypper commented Feb 23, 2023

Description

Please see the refit documents for this issue: https://github.com/reactiveui/refit

Situation

This is refit example showing us how to upload file through HTTP

public interface ISomeApi
{
    [Multipart]
    [Post("/users/{id}/photo")]
    Task UploadPhoto(int id, [AliasAs("myPhoto")] StreamPart stream);
}

The problem

Our API in Intranet Authentication Controller

    [AllowAnonymous]
    [HttpPost]
    public async Task<IActionResult> Register([FromForm] UserSignUpDTO dto, CancellationToken cancellationToken = default)
    {
        var user = new User()
        {
            UserName = dto.username,
            Email = dto.email
        };
        var result = await _userRepository.CreateAccount(user, dto.password);
        if (result.Succeeded)
        {
            if (dto.avatarfile is null) return Ok();
            if (_mediaService.IsImage(dto.avatarfile))
            {
                using (Stream stream = dto.avatarfile.OpenReadStream())
                {
                    Tuple<bool, string> uploadresults = await _mediaService.UploadAvatarToStorage(stream, dto.avatarfile.FileName);
                    var isUploaded = uploadresults.Item1;
                    var stringUrl = uploadresults.Item2;
                    if (isUploaded && !string.IsNullOrEmpty(stringUrl))
                    {
                        user.ProfilePic = stringUrl;
                        await _userRepository.UpdateUser(user, cancellationToken);
                        return Ok();
                    }
                    else return BadRequest("Look like the image couldnt upload to the storage, but your account have created successfully");
                }
            }
        }
        return BadRequest();
    }

UserSignUpDTO:

public record UserSignUpDTO(string username,
                            string password,
                            string firstname,
                            string lastname,
                            string email,
                            string phonenumber,
                            string[]? roles,
                            IFormFile? avatarfile)
{ }

As you can see the avatarfile is an IFormFile which we included inside this object, so far we haven't found a way to use refit to send this kind of object to our backend to recognize.

If we don't have a solution, we must call 2 requests to complete our registration process. we had the code for uploading files in using the System.Net.Http will be this massive like this

This is an example of my previous object uploading a file to ASP.NET

        public async Task<PetaverseMedia> CreatePetAvatarAsync(Animal petInfo, StorageFile avatar)
        {
            if (avatar != null)
            {
                var multipartFormContent = new MultipartFormDataContent();

                var handle = avatar.CreateSafeFileHandle(options: FileOptions.RandomAccess);
                var stream = new FileStream(handle, FileAccess.ReadWrite) { Position = 0 };
                var media = new StreamContent(stream);
                media.Headers.Add("Content-Type", "image/jpeg");
                multipartFormContent.Add(media, name: "avatar", fileName: $"{petInfo.Name}");

                try
                {
                    var result = await _httpClient.PostAsync($"api/Animal/UploadPetAvatar/{petInfo.Id}", multipartFormContent);
                    string stringReadResult = await result.Content.ReadAsStringAsync();
                    return JsonConvert.DeserializeObject<PetaverseMedia>(stringReadResult);
                }
                catch (Exception ex)
                {
                    await new HttpRequestErrorContentDialog()
                    {
                        Title = "Can't upload avatar"
                    }.ShowAsync();
                    return null;
                }
            }
            else return null;
        }

Public API Changes

        try
        {
            var response = await this.intranetAuthenticationRefit.Register(new RegisterDTO(userName, firstName, lastName, phoneNumber, email, password, profilePicStream));
            if (!response.IsSuccessStatusCode)
            {
                var errorContentJson = JsonConvert.DeserializeObject<RefitErrorMessageModel>(response.Error.Content);
                throw new Exception(errorContentJson.title);
            }
        }
        catch (ApiException ex)
        {
            throw new Exception(ex.Message);
        }

Intended Use-Case

Create user information with an avatar with only one call

@Strypper Strypper added enhancement 🔥 New feature or request new feature 🚀 New feature addition level/extreme 🥵 labels Feb 23, 2023
@Strypper Strypper added this to the Release 1.0 milestone Feb 23, 2023
@Strypper
Copy link
Owner Author

  1. Upload only the image in the body
  2. Upload with one parameter and 1 image in body
  3. Upload a complex object that has an IFormFile property inside it

@Strypper
Copy link
Owner Author

@duydh1995 have done the investigation about this issue and found out some of the ByteArrayPart and the StreamPart type
On the server, asp.net seem to accept the DTO
But on the front end in order to make create such an accepted request that it has to be in a format like below

    [Multipart]
    [Put("/User/TestUpload3")]
    Task TestUpload3([AliasAs("Id")] int id, [AliasAs("Name")] string name, [AliasAs("File")] StreamPart file);

I try do this for a cleaner code but the server quickly responded back 400 bad requests

    [Multipart]
    [Put("/User/TestUpload3")]
    Task TestUpload3(TestUploadByteArrayPartDTO1 dto);
public class TestUploadByteArrayPartDTO1
{
    [AliasAs("Id")]
    public int Id { get; set; }

    [AliasAs("Name")]
    public string Name { get; set; }

    [AliasAs("File")]
    public StreamPart File { get; set; }
};

@Strypper Strypper linked a pull request Feb 26, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement 🔥 New feature or request level/extreme 🥵 new feature 🚀 New feature addition
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

4 participants