Devolver contenido con IHttpActionResult para una respuesta no correcta

185

Para regresar de un controlador Web API 2, puedo devolver contenido con la respuesta si la respuesta es correcta (estado 200) de esta manera:

    public IHttpActionResult Get()
    {
        string myResult = ...
        return Ok(myResult);
    }

Si es posible, quiero usar los tipos de resultados integrados aquí cuando sea posible: https://msdn.microsoft.com/en-us/library/system.web.http.results(v=vs.118).aspx

Mi pregunta es, para otro tipo de respuesta (no 200), ¿cómo puedo devolver un mensaje (cadena) con él? Por ejemplo, puedo hacer esto:

    public IHttpActionResult Get()
    {
       return InternalServerError();
    }

pero no esto:

    public IHttpActionResult Get()
    {
       return InternalServerError("Message describing the error here");
    }

Idealmente, quiero que esto se generalice para poder enviar un mensaje de vuelta con cualquiera de las implementaciones de IHttpActionResult.

¿Necesito hacer esto (y construir mi propio mensaje de respuesta):

    public IHttpActionResult Get()
    {
       HttpResponseMessage responseMessage = ...
       return ResponseMessage(responseMessage);
    }

¿O hay un mejor camino?

mayabelle
fuente
¿no podría usar ApiController.InternalServerError msdn.microsoft.com/en-us/library/dn292630(v=vs.118).aspx
Ric
@ Milen, gracias. Algo así podría funcionar. La parte que no me gusta es que requiere crear una implementación IHttpActionResult diferente para cada implementación existente que quiero poder usar.
mayabelle
@Ric, no, el parámetro es una excepción. Quiero establecer un mensaje como una cadena. Además, esto no aborda un caso más general en el que el código no necesariamente sea un error interno del servidor.
mayabelle
3
@mayabelle: ¿Viste la respuesta de Shamil Yakupov? Es mucho más simple y conciso que la respuesta aceptada.
Isaac

Respuestas:

420

Puedes usar esto:

return Content(HttpStatusCode.BadRequest, "Any object");
Shamil Yakupov
fuente
1
Solución corta y simple. Tener más códigos significa más errores y mucho tiempo de mantenimiento.
Thomas.Benz
66
Cuando intento esto, el valor devuelto de code(donde el código es una cadena) return Content(HttpStatusCode.OK, code)se encapsula en "que es inesperado, ¿hay alguna razón para esto? Por ejemplo, el valor que se devuelve es "\"value\""que estoy usando mvc5
Deza
2
Si necesita hacer esto desde fuera de la clase ApiController, puede usar: return new NegotiatedContentResult <T> (código, nueva T (...), controlador)
Etherman
¿Puedo devolverlo de una biblioteca de clases? ¿Qué necesito hacer referencia?
Toolkit
54

Puede usar HttpRequestMessagesExtensions.CreateErrorResponse ( System.Net.Httpespacio de nombres), así:

public IHttpActionResult Get()
{
   return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Message describing the error here"));
}

Es preferible crear respuestas basadas en la solicitud para aprovechar la negociación de contenido de la API web.

usuario1620220
fuente
66
Request.CreateErrorResponse devuelve un mensaje HttpResponseMessage, no IHttpActionResult. Lo que describe es una buena práctica para crear un mensaje HttpResponseMessage pero no responde mi pregunta. ¡Gracias de cualquier manera!
mayabelle
@mayabelle puedes crear concreto IHttpActionResult y envolver esos códigos de esta manera:
Quoc Nguyen
1
Esto funcionó para mí, pero utilicé Request.CreateResponse para que el error se muestre como una cadena en lugar de bajo la tecla Mensaje.
Químico
Recibo un error, el fragmento falla. Dice 'solicitud' es nulo. Estoy tratando de usar Request.CreateResponse @ user1620220
Sheena Agrawal
@SheenaAgrawal Este código solo se puede ejecutar en el contexto de una solicitud HTTP. Si ApiController.Requestes nulo, significa que no está en el contexto correcto, o algo está roto en su arquitectura WebAPI.
user1620220
35

Terminé yendo con la siguiente solución:

public class HttpActionResult : IHttpActionResult
{
    private readonly string _message;
    private readonly HttpStatusCode _statusCode;

    public HttpActionResult(HttpStatusCode statusCode, string message)
    {
        _statusCode = statusCode;
        _message = message;
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage(_statusCode)
        {
            Content = new StringContent(_message)
        };
        return Task.FromResult(response);
    }
}

... que se puede usar así:

public IHttpActionResult Get()
{
   return new HttpActionResult(HttpStatusCode.InternalServerError, "error message"); // can use any HTTP status code
}

Estoy abierto a sugerencias de mejora. :)

mayabelle
fuente
1
La respuesta de Shamil Yakupov es la mejor respuesta, pero solo desde dentro de la clase ApiController: debe reescribirse como algo así como "return new NegotiatedContentResult <T> (código, nueva T (...), controlador)" para usarse desde fuera del clase de controlador En ese caso, una solución como esta anterior puede ser más legible.
Etherman
16

También puedes hacer:

return InternalServerError(new Exception("SOME CUSTOM MESSAGE"));
ilans
fuente
1
Sí, pero es
difícil
7

Cualquiera que esté interesado en devolver algo con cualquier código de estado con ResponseMessage:

//CreateResponse(HttpStatusCode, T value)
return ResponseMessage(Request.CreateResponse(HttpStatusCode.XX, object));
CularBytes
fuente
7

En ASP.NET Web API 2, puede ajustar cualquiera ResponseMessageen un ResponseMessageResult :

public IHttpActionResult Get()
{
   HttpResponseMessage responseMessage = ...
   return new ResponseMessageResult(responseMessage);
}

En algunos casos, esta puede ser la forma más sencilla de obtener el resultado deseado, aunque en general puede ser preferible utilizar los diversos resultados en System.Web.Http.Results .

sfuqua
fuente
6

Sencillo:

return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Your message"));

Recuerde hacer referencia a System.Net.Http y System.Net .

Rodrigo Reis
fuente
2

Un ejemplo más detallado con soporte de código HTTP no definido en C # HttpStatusCode.

public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        HttpStatusCode codeNotDefined = (HttpStatusCode)429;
        return Content(codeNotDefined, "message to be sent in response body");
    }
}

Contentes un método virtual definido en clase abstracta ApiController, la base del controlador. Vea la declaración a continuación:

protected internal virtual NegotiatedContentResult<T> Content<T>(HttpStatusCode statusCode, T value);
campo temático
fuente
1

@mayabelle puedes crear concreto IHttpActionResult y envolver esos códigos de esta manera:

public class NotFoundPlainTextActionResult : IHttpActionResult
{
    public NotFoundPlainTextActionResult(HttpRequestMessage request, string message)
    {
        Request = request;
        Message = message;
    }

    public string Message { get; private set; }
    public HttpRequestMessage Request { get; private set; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(ExecuteResult());
    }

    public HttpResponseMessage ExecuteResult()
    {
        var response = new HttpResponseMessage();

        if (!string.IsNullOrWhiteSpace(Message))
            //response.Content = new StringContent(Message);
            response = Request.CreateErrorResponse(HttpStatusCode.NotFound, new Exception(Message));

        response.RequestMessage = Request;
        return response;
    }
}
Quoc Nguyen
fuente
0

Yo tuve el mismo problema. Quiero crear un resultado personalizado para mis controladores api, para llamarlos como return Ok("some text");

Luego hice esto: 1) Crear un tipo de resultado personalizado con singletone

public sealed class EmptyResult : IHttpActionResult
{
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.NoContent) { Content = new StringContent("Empty result") });
    }
}

2) Crear controlador personalizado con nuevo método:

public class CustomApiController : ApiController
{
    public IHttpActionResult EmptyResult()
    {
        return new EmptyResult();
    }
}

Y luego puedo llamarlos en mis controladores, así:

public IHttpActionResult SomeMethod()
    {
       return EmptyResult();
    }
Merchezatter
fuente
0

Esta respuesta se basa en la respuesta de Shamil Yakupov, con objeto real en lugar de cadena.

using System.Dynamic;

dynamic response = new ExpandoObject();
response.message = "Email address already exist";

return Content<object>(HttpStatusCode.BadRequest, response);
Kugan Kumar
fuente
1
El contenido <T> es muy útil
LastTr tribunal
0

Por excepciones, generalmente hago

 catch (Exception ex)
        {
            return InternalServerError(new ApplicationException("Something went wrong in this request. internal exception: " + ex.Message));
        }
ahsant
fuente
0

Las cosas anteriores son realmente útiles.

Al crear servicios web, si toma el caso de los servicios, el consumidor lo apreciará mucho. Traté de mantener la uniformidad de la salida. También puede dar un comentario o un mensaje de error real. El consumidor del servicio web solo puede verificar que IsSuccess sea verdadero o no, de lo contrario se asegurará de que haya un problema y actuará según la situación.

  public class Response
    {
        /// <summary>
        /// Gets or sets a value indicating whether this instance is success.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is success; otherwise, <c>false</c>.
        /// </value>
        public bool IsSuccess { get; set; } = false;

        /// <summary>
        /// Actual response if succeed 
        /// </summary>
        /// <value>
        /// Actual response if succeed 
        /// </value>
        public object Data { get; set; } = null;

        /// <summary>
        /// Remark if anythig to convey
        /// </summary>
        /// <value>
        /// Remark if anythig to convey
        /// </value>
        public string Remark { get; set; } = string.Empty;
        /// <summary>
        /// Gets or sets the error message.
        /// </summary>
        /// <value>
        /// The error message.
        /// </value>
        public object ErrorMessage { get; set; } = null;


    }  




[HttpGet]
        public IHttpActionResult Employees()
        {
            Response _res = new Response();
            try
            { 
                DalTest objDal = new DalTest(); 
                _res.Data = objDal.GetTestData();
                _res.IsSuccess = true;
                return Ok<Response>(_res);
            }
            catch (Exception ex)
            {
                _res.IsSuccess = false;
                _res.ErrorMessage = ex;
                return ResponseMessage(Request.CreateResponse(HttpStatusCode.InternalServerError, _res )); 
            } 
        }

Le invitamos a dar sugerencias si las hay :)

Amol Khandagale
fuente
-1

Perdón por la respuesta tardía, ¿por qué no usas simplemente

return BadRequest("your message");

Lo uso para todos mis IHttpActionResulterrores, funciona bien

Aquí está la documentación: https://msdn.microsoft.com/en-us/library/system.web.http.apicontroller.badrequest(v=vs.118).aspx

benraay
fuente
77
Debido a que no todos los errores son el resultado de solicitudes incorrectas, una 400respuesta sería inapropiada. OP dio específicamente una 500respuesta como ejemplo.
user1620220
Sí, solo es posible con BadRequest, los otros tipos no aceptan un argumento de mensaje
benraay