Carga de archivos de Angular a ASP.NET Core
Por primera vez, estoy tratando de cargar un archivo desde un componente angular a una página web ASPNET Core y simplemente no puedo hacer que funcione. Con suerte, los siguientes extractos de código serán suficientes para mostrar lo esencial de lo que está sucediendo. El problema es que, aunque confirmo que el parámetro pasado al método de publicación de HttpClient (frmData) es válido, el método de acción ASPNet Core nunca lo ve e informa que IFormFile siempre es nulo.
EDITAR: Anteriormente había intentado usar multipart / form-data como tipo de contenido, pero di una excepción no controlada en las entrañas de Kestrel. Ahora me doy cuenta de que esta es la forma correcta de hacerlo, y el uso del tipo de contenido json fue la fuente de mi problema ORIGINAL. Pero no sé a dónde ir desde aquí. Veo por algunas búsquedas en Google que hay alrededor de mil millones de causas diferentes para que ocurra esa excepción.
POST Ejecución del punto final 'JovenesA.Controllers.StudentssController.PostStudentGradesReport (JAWebAPI)' 04: 55: 38.4853 Info ControllerActionInvoker POST Ruta que coincide con {action = "PostStudentGradesReport", controller = "Becas"}. Ejecución de la acción JovenesA.Controllers.BecasController.PostStudentGradesReport (JAWebAPI) 04: 55: 38.5032 Error DeveloperExceptionPageMiddleware POST Se ha producido una excepción no controlada al ejecutar la solicitud. 04: 55: 38.5333 Información WebHost Solicitud POST finalizada en 48,1225 ms 500 texto / html; juego de caracteres = utf-8 04: 55: 38.5333 Información Kestrel Id. De conexión "0HM4UHGE85O17", Id. De solicitud "0HM4UHGE85O17: 00000006": la solicitud se completó sin leer todo el cuerpo de la solicitud.
¡Cualquier ayuda será muy apreciada!
Componente angular:
fileEntry.file((file: File) => {
console.log('fileEntry relativePath: ' + currFile.relativePath);
console.log('filEntry.name: ', file.name);
console.log('filEntry.size: ', file.size);
const frmData = new FormData();
frmData.append(file.name, file);
this.studentData.uploadStudentGradesReport(file.name, frmData).subscribe(
() => {
this.successMessage = 'Changes were saved successfully.';
window.scrollTo(0, 0);
window.setTimeout(() => {
this.successMessage = '';
}, 3000);
},
(error) => {
this.errorMessage = error;
}
);
});
Servicio Angular:
public uploadStudentGradesReport(filename: string, frmData: FormData): Observable<any> {
const url = this.WebApiPrefix + 'students/' + 'student-grades-report';
const headers = new HttpHeaders().set('Content-Type', 'application/json');
if (frmData) {
console.log('ready to post ' + url + ' filename: ' + filename + ' options ' + headers);
return this.http.post(url, frmData, { headers });
}
}
Control de núcleo ASPNET
// POST api/students/student-grades-report
[HttpPost("student-grades-report", Name = "PostStudentGradseReportRoute")]
//[ValidateAntiForgeryToken]
[ProducesResponseType(typeof(GradesGivenEntryApiResponse), 200)]
[ProducesResponseType(typeof(GradesGivenEntryApiResponse), 400)]
public async Task<ActionResult> PostStudentGradesReport([FromForm] IFormFile myFile)
{
_Logger.LogInformation("Post StudentGradesReport ");
if (myFile != null)
{
var totalSize = myFile.Length;
var fileBytes = new byte[myFile.Length];
Si ayuda, aquí están los datos que se envían en la solicitud POST
POST http://192.168.0.16:1099/api/students/student-grades-report HTTP / 1.1 Anfitrión: 192.168.0.16:1099 Conexión: mantener vivo Longitud del contenido: 13561 Aceptar: application / json, text / plain, * / * DNT: 1 Usuario-Agente: Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36 (KHTML, como Gecko) Chrome / 87.0.4280.88 Safari / 537.36 Tipo de contenido: aplicación / json Origen: http: // localhost: 3000 Referente: http: // localhost: 3000 / Aceptar codificación: gzip, desinflar Aceptar-Idioma: en-US, en; q = 0.9, es-MX; q = 0.8, es; q = 0.7 ------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a Disposición de contenido: datos de formulario; nombre = "test1.PNG"; filename = "test1.PNG" Tipo de contenido: imagen / png PNG [contenido binario del archivo de imagen] ------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a--
Respuestas
Está enviando el archivo como datos de formulario, por lo que debe especificar el encabezado del tipo de contenido correcto. Actualmente su envío application/json
en el Content-Type
encabezado. Esto es cierto incluso cuando se llama a una API, lo que comprensiblemente puede resultar confuso al principio. El tipo de contenido correcto en este caso es multipart/form-data
. Su API no ve el IFormFile
porque cree que la solicitud es JSON. Modifiqué su código angular con el valor de encabezado de tipo de contenido correcto.
Editar: Resulta que especificar manualmente un Content-Type
encabezado hará que los valores de los límites no se establezcan automáticamente en el valor del encabezado. En cambio, la solución simple es no agregar el encabezado usted mismo, lo que dará como resultado que se establezcan automáticamente los valores de límite y tipo de contenido adecuados. Si configura el encabezado usted mismo, también tendrá que configurar los valores de los límites. Para la mayoría de las situaciones, dejarlo por defecto es probablemente la mejor solución. Enlace a la pregunta / respuesta que señala esto. FormData cómo obtener o establecer el límite en multipart / form-data - Angular
public uploadStudentGradesReport(filename: string, frmData: FormData): Observable<any> {
const url = this.WebApiPrefix + 'students/' + 'student-grades-report';
const headers = new HttpHeaders().set('Content-Type', 'multipart/form-data');
if (frmData) {
console.log('ready to post ' + url + ' filename: ' + filename + ' options ' + headers);
return this.http.post(url, frmData, { headers });
}
}
También puede observar la disposición del contenido que se encuentra en la solicitud HTTP que proporcionó, que muestra los datos del formulario junto con el tipo de archivo adjunto. Espero que esto ayude. No encendí un proyecto Angular para probar su código, pero el tipo de contenido debería solucionar su problema.
Editar : noté que estás usando el nombre del archivo como clave para el campo del formulario con el archivo. Necesita usar una clave como 'archivo' para el campo del formulario, que debe coincidir con el nombre del parámetro en su código de controlador. Puede obtener el nombre de archivo real del archivo dentro de su código de controlador, la clave simplemente indica a qué campo de formulario están adjuntos los archivos. Ejemplo
frmData.append('file', file);
Y luego para la acción de tu controlador
public async Task<IActionResult> PostStudentGradesReport([FromForm] IFormFile file)
{
if (file.Length <= 0 || file.ContentType is null) return BadRequest();
var actualFileName = file.FileName;
using (var stream = file.OpenReadStream())
{
// Process file...
}
return Ok();
}
No puedo garantizar que esto funcione, pero puede intentar usar HttpRequest de Angular. Entonces, en su servicio angular, intente esto:
const request = new HttpRequest (
'POST',
url, // http://localhost/your_endpoint
frmData,
{ withCredentials: false }
);
return this.http.request(request);
También tenga en cuenta que no debe realizar la validación de datos en la función que llama a la API de backend. ¿Cuál es la función que devolverá si if(frmData)
es falsa?