Estamos desenvolvendo servidor com API REST, que aceita e responde com JSON. O problema é que, se você precisar fazer upload de imagens do cliente para o servidor.
Nota: e também estou falando de um caso de uso em que a entidade (usuário) pode ter vários arquivos (carPhoto, licensePhoto) e também ter outras propriedades (nome, email ...), mas quando você cria um novo usuário, você não enviar essas imagens, elas são adicionadas após o processo de registro.
As soluções que eu conheço, mas cada uma delas tem algumas falhas
1. Use multipart / form-data em vez de JSON
good : as solicitações POST e PUT são o mais RESTful possível, elas podem conter entradas de texto junto com o arquivo.
contras : não é mais JSON, o que é muito mais fácil de testar, depurar etc. comparar com dados de várias partes / formulário
2. Permitir atualizar arquivos separados
A solicitação POST para criar novo usuário não permite adicionar imagens (o que é bom em nosso caso de uso, como eu disse no início), o upload de imagens é feito pela solicitação PUT como multipart / form-data para, por exemplo / users / 4 / carPhoto
good : tudo (exceto o arquivo que está sendo carregado) permanece em JSON, é fácil testar e depurar (você pode registrar solicitações JSON completas sem ter medo do tamanho)
contras : Não é intuitivo, você não pode POST ou PUT todas as variáveis da entidade de uma só vez e também esse endereço /users/4/carPhoto
pode ser considerado mais uma coleção (o caso de uso padrão da API REST se parece com isso /users/4/shipments
). Normalmente você não pode (e não quer) GET / PUT cada variável da entidade, por exemplo users / 4 / name. Você pode obter um nome com GET e alterá-lo com PUT em users / 4. Se houver algo após o ID, geralmente haverá outra coleção, como users / 4 / reviews
3. Use Base64
Envie-o como JSON, mas codifique arquivos com o Base64.
good : Igual à primeira solução, é o serviço RESTful possível.
contras : Mais uma vez, o teste e a depuração são muito piores (o corpo pode ter megabytes de dados), há um aumento no tamanho e também no tempo de processamento em ambos - cliente e servidor
Eu realmente gostaria de usar a solução não. 2, mas tem seus contras ... Qualquer pessoa pode me dar uma idéia melhor da solução "o que é melhor"?
Meu objetivo é ter serviços RESTful com o máximo possível de padrões incluídos, enquanto eu quero mantê-lo o mais simples possível.
Respostas:
OP aqui (estou respondendo a essa pergunta após dois anos, o post feito por Daniel Cerecedo não era ruim de uma vez, mas os serviços da web estão se desenvolvendo muito rápido)
Após três anos de desenvolvimento de software em tempo integral (com foco também na arquitetura de software, gerenciamento de projetos e arquitetura de microsserviços), eu definitivamente escolho a segunda maneira (mas com um ponto de extremidade geral) como a melhor.
Se você possui um ponto final especial para imagens, ele oferece muito mais poder sobre o manuseio dessas imagens.
Temos a mesma API REST (Node.js) para aplicativos móveis (iOS / android) e front-end (usando o React). Este é o ano de 2017, portanto, você não deseja armazenar imagens localmente, deseja enviá-las para algum armazenamento na nuvem (Google cloud, s3, cloudinary, ...); portanto, deseja um tratamento geral sobre elas.
Nosso fluxo típico é que, assim que você seleciona uma imagem, ela começa a ser carregada em segundo plano (geralmente POST no terminal / imagens), retornando o ID após o upload. Isso é realmente fácil de usar, porque o usuário escolhe uma imagem e, em seguida, geralmente prossegue com outros campos (por exemplo, endereço, nome, ...); portanto, quando ele pressiona o botão "enviar", a imagem já está carregada. Ele não espera e vê a tela dizendo "enviando ...".
O mesmo vale para obter imagens. Especialmente graças aos telefones celulares e aos dados móveis limitados, você não deseja enviar imagens originais, deseja redimensionar imagens, para que elas não ocupem tanta largura de banda (e para tornar seus aplicativos móveis mais rápidos, muitas vezes você não deseja para redimensioná-lo, você deseja a imagem que se encaixa perfeitamente na sua exibição). Por esse motivo, bons aplicativos estão usando algo como cloudinary (ou temos nosso próprio servidor de imagem para redimensionar).
Além disso, se os dados não forem privados, você envia de volta ao URL do aplicativo / front-end e o baixa diretamente do armazenamento na nuvem, o que é uma enorme economia de largura de banda e tempo de processamento para o servidor. Em nossos aplicativos maiores, há muitos terabytes baixados todos os meses; você não deseja lidar com isso diretamente em cada servidor da API REST, focado na operação de CRUD. Você deseja lidar com isso em um único local (nosso servidor de imagens, que possui cache etc.) ou permitir que os serviços em nuvem lidem com tudo isso.
Contras: Os únicos "contras" em que você deve pensar são "imagens não atribuídas". O usuário seleciona imagens e continua preenchendo outros campos, mas ele diz "nah" e desativa o aplicativo ou a guia, mas, enquanto isso, você carregou a imagem com sucesso. Isso significa que você enviou uma imagem que não foi atribuída a nenhum lugar.
Existem várias maneiras de lidar com isso. O mais fácil é "Não me importo", o que é relevante, se isso não acontecer com muita frequência ou você desejar armazenar todos os usuários de imagens que você enviar (por qualquer motivo) e você não desejar eliminação.
Outra também é fácil - você tem CRON e, a cada semana, e exclui todas as imagens não atribuídas com mais de uma semana.
fonte
Existem várias decisões a serem tomadas :
O primeiro sobre o caminho do recurso :
Modele a imagem como um recurso por si só:
Aninhado no usuário (/ user /: id / image): o relacionamento entre o usuário e a imagem é feito implicitamente
No caminho raiz (/ image):
O cliente é responsável por estabelecer o relacionamento entre a imagem e o usuário, ou;
Se um contexto de segurança estiver sendo fornecido com a solicitação POST usada para criar uma imagem, o servidor poderá estabelecer implicitamente um relacionamento entre o usuário autenticado e a imagem.
Incorporar a imagem como parte do usuário
A segunda decisão é sobre como representar o recurso de imagem :
Esta seria a minha pista de decisão:
Depois vem a pergunta: Existe algum impacto no desempenho sobre a escolha de base64 versus multipart? . Poderíamos pensar que a troca de dados no formato multipartes deveria ser mais eficiente. Mas este artigo mostra quão pouco as duas representações diferem em termos de tamanho.
Minha escolha Base64:
fonte
Sua segunda solução é provavelmente a mais correta. Você deve usar a especificação HTTP e os tipos MIME da maneira como foram planejados e fazer upload do arquivo via
multipart/form-data
. Quanto a lidar com os relacionamentos, eu usaria esse processo (tendo em mente que não sei nada sobre suas suposições ou design do sistema):POST
para/users
criar a entidade do usuário.POST
a imagem para/images
, certificando-se de retornar umLocation
cabeçalho para onde a imagem pode ser recuperada de acordo com a especificação HTTP.PATCH
para/users/carPhoto
e atribuí-lo a identificação da foto dada noLocation
cabeçalho do passo 2.fonte
Não há solução fácil. Cada caminho tem seus prós e contras. Mas a maneira canônica está usando a primeira opção:
multipart/form-data
. Como o guia de recomendação do W3 dizNa verdade, não estamos enviando formulários, mas o princípio implícito ainda se aplica. Usar base64 como uma representação binária está incorreto porque você está usando a ferramenta incorreta para atingir seu objetivo. Por outro lado, a segunda opção força seus clientes da API a realizar mais tarefas para consumir seu serviço de API. Você deve fazer o trabalho duro no lado do servidor para fornecer uma API fácil de consumir. A primeira opção não é fácil de depurar, mas quando você faz isso, provavelmente nunca muda.
Usando
multipart/form-data
você está preso à filosofia REST / http. Você pode ver uma resposta para uma pergunta semelhante aqui .Outra opção, se você combinar as alternativas, poderá usar dados de várias partes / formulário, mas, em vez de enviar todos os valores separadamente, poderá enviar um valor denominado payload com a json payload dentro dele. (Tentei essa abordagem usando o ASP.NET WebAPI 2 e funciona bem).
fonte