การส่งคืนรหัสสถานะ http จากตัวควบคุม Web Api


219

ฉันกำลังพยายามส่งคืนรหัสสถานะ 304 ที่ไม่ได้แก้ไขสำหรับวิธีการ GET ในตัวควบคุมเว็บ api

วิธีเดียวที่ฉันประสบความสำเร็จคือสิ่งนี้:

public class TryController : ApiController
{
    public User GetUser(int userId, DateTime lastModifiedAtClient)
    {
        var user = new DataEntities().Users.First(p => p.Id == userId);
        if (user.LastModified <= lastModifiedAtClient)
        {
             throw new HttpResponseException(HttpStatusCode.NotModified);
        }
        return user;
    }
}

ปัญหาที่นี่คือมันไม่ใช่ข้อยกเว้นมันไม่ได้ถูกแก้ไขดังนั้นแคชของลูกค้าก็โอเค ฉันต้องการให้ประเภทการคืนเป็นผู้ใช้ด้วย (เนื่องจากเว็บ api ทุกตัวอย่างแสดงด้วย GET) ไม่ส่งคืน HttpResponseMessage หรืออะไรทำนองนี้


คุณใช้betaหรือสร้างงานทุกคืนหรือไม่?
Aliostad

@Aliostad ฉันใช้เบต้า
ozba

แล้วเกิดความผิดปกติอะไรกับการกลับมาnew HttpResponseMessage(HttpStatusCode.NotModified)? มันใช้งานไม่ได้?
Aliostad

@Aliostad ฉันไม่สามารถส่งคืน HttpResponseMessage เมื่อประเภทส่งคืนคือผู้ใช้มันไม่ได้รวบรวม (ชัด)
ozba

คำตอบ:


251

ผมไม่ทราบว่าคำตอบเพื่อให้ถามทีมงาน ASP.NET ที่นี่

ดังนั้นเคล็ดลับคือการเปลี่ยนลายเซ็นไปและการใช้งานHttpResponseMessageRequest.CreateResponse

[ResponseType(typeof(User))]
public HttpResponseMessage GetUser(HttpRequestMessage request, int userId, DateTime lastModifiedAtClient)
{
    var user = new DataEntities().Users.First(p => p.Id == userId);
    if (user.LastModified <= lastModifiedAtClient)
    {
         return new HttpResponseMessage(HttpStatusCode.NotModified);
    }
    return request.CreateResponse(HttpStatusCode.OK, user);
}

3
มันไม่ได้รวบรวมใน ASP.NET MVC 4 รุ่นเบต้าเนื่องจาก CreateResponse รับรหัสสถานะเป็นพารามิเตอร์เท่านั้น ประการที่สองฉันต้องการโซลูชันที่ไม่มี HttpResponseMessage เป็นค่าส่งคืนเนื่องจากเลิกใช้แล้ว: aspnetwebstack.codeplex.com/discussions/350492
ozba

5
ในกรณีที่ทุกคนต้องการเพื่อรับค่าจากวิธีการควบคุมจะเป็นGetUser(request, id, lastModified).TryGetContentValue(out user)ที่user(ในกรณีตัวอย่าง) เป็นUserวัตถุ
Grinn

4
นี่เป็นวิธีที่ต้องการในปี 2558 หรือไม่? MVC 5
บดขยี้

4
รุ่นที่ทันสมัยกว่าส่งคืน IHttpActionResult - ไม่ใช่ HttpResponseMessage (2017)
niico

8
หากต้องการเพิ่มข้อเสนอแนะ niico เมื่อชนิดกลับเป็นและคุณต้องการที่จะกลับผู้ใช้คุณก็สามารถทำได้IHttpActionResult return Ok(user)หากคุณจำเป็นต้องส่งคืนรหัสสถานะอื่น (พูดต้องห้าม) return this.StatusCode(HttpStatusCode.Forbidden)คุณก็สามารถทำได้
ดึง

68

คุณสามารถทำสิ่งต่อไปนี้หากคุณต้องการเก็บลายเซ็นการกระทำไว้เป็นผู้ใช้ที่กลับมา:

public User GetUser(int userId, DateTime lastModifiedAtClient) 

หากคุณต้องการส่งคืนสิ่งอื่นนอกจาก200นั้นคุณจะส่งการHttpResponseExceptionกระทำของคุณและส่งผ่านสิ่งที่HttpResponseMessageคุณต้องการส่งให้ลูกค้า


9
นี่เป็นวิธีการแก้ปัญหาที่สง่างามกว่า ทำไมทุกคนถึงชอบทำอย่างหนัก
nagytech

4
@Geoist stackoverflow.com/questions/1282252/… . การโยนข้อยกเว้นมีค่าใช้จ่ายสูง
tia

10
ใช่ถ้าคุณกำลังออกแบบ API ที่ไม่ว่างการใช้ข้อยกเว้นในการสื่อสารกรณีที่พบบ่อยที่สุดNotModifiedนั้นสิ้นเปลืองจริงๆ หาก API ทั้งหมดของคุณทำเช่นนี้เซิร์ฟเวอร์ของคุณจะแปลงวัตต์เป็นข้อยกเว้นเป็นส่วนใหญ่
ลุค Puplett

2
@nagytech เนื่องจากคุณไม่สามารถส่งคืนข้อความแสดงข้อผิดพลาดที่กำหนดเองได้หากคุณส่งข้อผิดพลาด (เช่นการตอบสนอง 400 ครั้ง) ... การขว้างข้อยกเว้นเป็นเรื่องโง่สำหรับสิ่งที่คุณคาดหวังว่ารหัสจะต้องทำ มีราคาแพงและจะถูกบันทึกไว้เมื่อคุณไม่ต้องการให้เป็น พวกเขาไม่ได้ยกเว้นจริงๆ
Rocklan

40

ใน MVC 5 สิ่งต่าง ๆ ง่ายขึ้น:

return new StatusCodeResult(HttpStatusCode.NotModified, this);

3
ไม่สามารถระบุข้อความได้ใช่ไหม
ทำลาย

1
การใช้ข้อความเป็นคำตอบที่ยอมรับได้จริง นี่เป็นเพียงผู้กระเซ็นเล็กน้อย
Jon Bates

39

เปลี่ยนเมธอด GetXxx API เพื่อส่งคืน HttpResponseMessage จากนั้นส่งคืนเวอร์ชันที่พิมพ์สำหรับการตอบกลับแบบเต็มและเวอร์ชันที่ไม่ได้พิมพ์สำหรับการตอบกลับ NotModified

    public HttpResponseMessage GetComputingDevice(string id)
    {
        ComputingDevice computingDevice =
            _db.Devices.OfType<ComputingDevice>()
                .SingleOrDefault(c => c.AssetId == id);

        if (computingDevice == null)
        {
            return this.Request.CreateResponse(HttpStatusCode.NotFound);
        }

        if (this.Request.ClientHasStaleData(computingDevice.ModifiedDate))
        {
            return this.Request.CreateResponse<ComputingDevice>(
                HttpStatusCode.OK, computingDevice);
        }
        else
        {
            return this.Request.CreateResponse(HttpStatusCode.NotModified);
        }
    }

* ข้อมูล ClientHasStale เป็นส่วนขยายของฉันสำหรับการตรวจสอบ ETag และ IfModifiedSince ส่วนหัว

กรอบงาน MVC ควรเป็นอันดับและส่งคืนวัตถุของคุณ

บันทึก

ฉันคิดว่าเวอร์ชันทั่วไปกำลังถูกลบใน Web API เวอร์ชันต่อ ๆ ไป


4
นี่คือคำตอบที่ฉันต้องการ - แม้ว่าจะเป็นประเภท <HttpResponseMessage <T>> ประเภทงานคืน ขอบคุณ!
xeb

1
@ xeb - ใช่ว่าเป็นมูลค่าการโทรออกทั้งหมด ข้อมูลเพิ่มเติมเกี่ยวกับ async ที่นี่ asp.net/mvc/tutorials/mvc-4/…
Luke Puplett

14

ฉันเกลียดการชนบทความเก่า แต่นี่เป็นผลลัพธ์แรกของเรื่องนี้ในการค้นหาของ Google และฉันมีเวลากับปัญหานี้ (แม้จะได้รับการสนับสนุนจากพวกคุณ) ดังนั้นที่นี่ไม่มีอะไรไป ...

หวังว่าโซลูชันของฉันจะช่วยให้ผู้ที่สับสนเช่นกัน

namespace MyApplication.WebAPI.Controllers
{
    public class BaseController : ApiController
    {
        public T SendResponse<T>(T response, HttpStatusCode statusCode = HttpStatusCode.OK)
        {
            if (statusCode != HttpStatusCode.OK)
            {
                // leave it up to microsoft to make this way more complicated than it needs to be
                // seriously i used to be able to just set the status and leave it at that but nooo... now 
                // i need to throw an exception 
                var badResponse =
                    new HttpResponseMessage(statusCode)
                    {
                        Content =  new StringContent(JsonConvert.SerializeObject(response), Encoding.UTF8, "application/json")
                    };

                throw new HttpResponseException(badResponse);
            }
            return response;
        }
    }
}

และจากนั้นก็รับมรดกจาก BaseController

[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...

แล้วใช้มัน

[HttpGet]
[Route("device/search/{property}/{value}")]
public SearchForDeviceResponse SearchForDevice(string property, string value)
{
    //todo: limit search property here?
    var response = new SearchForDeviceResponse();

    var results = _deviceManagementBusiness.SearchForDevices(property, value);

    response.Success = true;
    response.Data = results;

    var statusCode = results == null || !results.Any() ? HttpStatusCode.NoContent : HttpStatusCode.OK;

    return SendResponse(response, statusCode);
}

1
สุกใส ช่วยฉันทีหนึ่งครั้ง
gls123

10

.net core 2.2 ส่งคืนรหัสสถานะ 304 นี่คือการใช้ ApiController

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304);
    }

เป็นทางเลือกคุณสามารถส่งคืนวัตถุที่มีการตอบสนอง

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304, YOUROBJECT); 
    }

7

สำหรับเว็บ ASP.NET Api 2 โพสต์นี้จาก MSIHttpActionResultแนะนำให้เปลี่ยนประเภทการกลับมาของวิธีการที่จะ จากนั้นคุณสามารถกลับมาที่สร้างขึ้นในIHttpActionResultการดำเนินงานเช่นOk, BadRequestฯลฯ ( ดูที่นี่ ) หรือกลับการดำเนินงานของคุณเอง

สำหรับรหัสของคุณมันสามารถทำได้เช่น:

public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
{
    var user = new DataEntities().Users.First(p => p.Id == userId);
    if (user.LastModified <= lastModifiedAtClient)
    {
        return StatusCode(HttpStatusCode.NotModified);
    }
    return Ok(user);
}

3

ตัวเลือกอื่น:

return new NotModified();

public class NotModified : IHttpActionResult
{
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotModified);
        return Task.FromResult(response);
    }
}

2
public HttpResponseMessage Post(Article article)
{
    HttpResponseMessage response = Request.CreateResponse<Article>(HttpStatusCode.Created, article);

    string uriToTheCreatedItem = Url.Route(null, new { id = article.Id });
    response.Headers.Location = new Uri(Request.RequestUri, uriToTheCreatedItem);

    return response;
}

2

หากคุณต้องการส่งคืน IHttpActionResult และต้องการส่งคืนรหัสข้อผิดพลาดพร้อมข้อความให้ใช้:

return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));

2

ฉันไม่ต้องการเปลี่ยนลายเซ็นของฉันเพื่อใช้ประเภท HttpCreateResponse ดังนั้นฉันจึงคิดวิธีแก้ปัญหาเพิ่มเติมเล็กน้อยเพื่อซ่อนมัน

public class HttpActionResult : IHttpActionResult
{
    public HttpActionResult(HttpRequestMessage request) : this(request, HttpStatusCode.OK)
    {
    }

    public HttpActionResult(HttpRequestMessage request, HttpStatusCode code) : this(request, code, null)
    {
    }

    public HttpActionResult(HttpRequestMessage request, HttpStatusCode code, object result)
    {
        Request = request;
        Code = code;
        Result = result;
    }

    public HttpRequestMessage Request { get; }
    public HttpStatusCode Code { get; }
    public object Result { get; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Request.CreateResponse(Code, Result));
    }
}

จากนั้นคุณสามารถเพิ่มวิธีการใน ApiController ของคุณ (หรือดีกว่าตัวควบคุมฐานของคุณ) เช่นนี้:

protected IHttpActionResult CustomResult(HttpStatusCode code, object data) 
{
    // Request here is the property on the controller.
    return new HttpActionResult(Request, code, data);
}

จากนั้นคุณสามารถส่งคืนได้เหมือนวิธีการที่มีในตัว:

[HttpPost]
public IHttpActionResult Post(Model model)
{
    return model.Id == 1 ?
                Ok() :
                CustomResult(HttpStatusCode.NotAcceptable, new { 
                    data = model, 
                    error = "The ID needs to be 1." 
                });
}

0

การอัปเดตเป็นคำตอบ @Aliostads โดยใช้ moden เพิ่มเติม IHttpActionResultแนะนำใน Web API 2

https://docs.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/action-results#ihttpactionresult

public class TryController : ApiController
{
    public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
    {
        var user = new DataEntities().Users.First(p => p.Id == userId);
        if (user.LastModified <= lastModifiedAtClient)
        {
            return StatusCode(HttpStatusCode.NotModified);
            // If you would like to return a Http Status code with any object instead:
            // return Content(HttpStatusCode.InternalServerError, "My Message");
        }
        return Ok(user);
    }
}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.