2

I'm running through a list of both secure and unsecured domains (both http:// and https://) in an array and wish to return their status codes. How is this possible in ASP.Net Core 1.0?

so far, I have

foreach(var item in _context.URLs.ToList())
{
    // Do something here with item.Domain to check for status
    // For example, if item.Domain was https://example.com..
}

I tried it with regular ASP.Net syntax with this approach:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(item.Domain);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

The problem is, GetResponse doesn't work in ASP.Net Core

Can anyone help me out with an effecient solution so that the variable returned would be the status?

ex: 200, 500, 404..

EDIT - Here is my full controller AND SOLUTION:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using MyApp.Models;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
namespace MyApp.Controllers.Api
{
    public class URLsController : Controller
    {
        private MyAppDBContext _context;

        public class newLink
        {
            public string Domain { get; set; }
            public HttpStatusCode Status { get; set; }
        }

        public async Task<HttpStatusCode> GetStatusCodes(string url)
        {
            var client = new HttpClient();
            var response = await client.GetAsync(url);

            return response.StatusCode;
        }

        public URLsController(MyAppDBContext context)
        {
            _context = context;
        }

        [HttpPost("api/URLs")]
        public async Task<IActionResult> Post(string url)
        {
            if (url != "")
            {
                // I pass a URL in through this API and add it to _context.URLs..
                // I execute a status code check on the URLs after this if statement
            }

            List<newLink> list = new List<newLink> ();

            foreach (var item in _context.URLs.ToList())
            {
                newLink t = new newLink();

                t.Domain = item.Domain;
                t.Status = await GetStatusCodes(item.Domain);

                list.Add(t);
            }

            return Ok(list);
        }
    }
}

This returns an array back in this format:

[{"Domain":"https://example1.com/","Status":200},

{"Domain":"https://example2.com/","Status":200},

{"Domain":"https://example3.com/","Status":200}]

5
  • Please add code for the controller method. Commented Jan 23, 2017 at 17:05
  • What if the domain from the url is not resolvable or your application doesn't have network access to it? What status code do you expect to have in this case? Commented Jan 23, 2017 at 17:07
  • 1
    What do you mean "doesn't work"? The method is there and there's no other way to get a response. If this doesn't return what you expected, you'll have to post what the actual problem is. Did you get an exception? Did StatusCode return something unexpected? Are you sure you used a correct URL? Commented Jan 23, 2017 at 17:09
  • 1
    Is the issue that you now need to use the async method instead? So GetResponseAsync instead of GetResponse? But you should probably look at using HttpClient anyway. (cc @PanagiotisKanavos It's not there in core, there's only async now) Commented Jan 23, 2017 at 17:11
  • @ChirdeepTomar I included my full controller. Commented Jan 23, 2017 at 19:38

1 Answer 1

8

You could use HttpClient as it is easier to work with (you don't need to catch WebExceptions for the non success status codes like you would have to do with the plain HttpWebRequest and then extract the HTTP status code from the exception).

You could write a helper method that given a list of urls will return a list of corresponding status codes. This will make your code a little bit more decoupled. Do not violate the single responsibility principle. A method should not do more than 1 specific thing (in your example you were mixing some DB calls and HTTP calls into a single method which is bad practice).

public async Task<IList<HttpStatusCode>> GetStatusCodes(IList<string> urls)
{
    var client = new HttpClient();
    var result = new List<HttpStatusCode>();
    foreach (var url in urls)
    {
        var response = await client.GetAsync(url);
        result.Add(response.StatusCode);
    }

    return result;
}

Remark 1: If the url that you are trying to call is not DNS resolvable or your calling application doesn't have network access to the specified address on the target port you will not get any status code for obvious reasons. You will get a nice exception. Think about handling this case. It's up to you to decide what you want to return in the resulting collection in this case.

Remark 2: Making a GET request just for determining the HTTP status code might be a waste as you are throwing away the response body that you have already transported over the wire. If the remote resource responds to HEAD requests that might be more efficient way to determine if the server is alive. But consider this with caution as it will depend on the specifics of the web endpoint that you are calling.

Remark 3: You have undoubtedly noticed that this method is async. Well, if you intend to develop under .NET Core you'd better get accustomed to it. You could of course violate the built-in asynchronous patterns that the framework provides you by blocking the calling thread:

var urls = _context.URLs.ToList();
IList<HttpStatusCode> statusCodes = GetStatusCodes(urls).GetAwaiter().GetResult();

But this is an extremely bad practice. A more idiomatic way of working is to make all your methods asynchronous through the entire chain until you reach the main calling method which is usually provided by the framework itself and which can be asynchronous as well. For example if you are calling this inside a Web API action you could simply make it async:

[HttpGet]
[Route("api/foos")]
public async Task<IActionResult> Get()
{
    var urls = _context.URLs.ToList();
    IList<HttpStatusCode> statusCodes = await GetStatusCodes(urls);
    return this.Ok(statusCodes);
}
Sign up to request clarification or add additional context in comments.

7 Comments

This is fantastic, but for some reason every which way I tried to implement your code, I kept generating errors. I've included my full controller for you to see.. Hopefully you can help me with where I can put your methods without generating any errors.
To be more precise if I add an async method before my main API method, it gives me an async error. If I try to do everything through my POST method, there's an error with add and async.
Let me know if you want to see my full code on trying out YOUR solution with my code, and what errors I see.
Did you read my answer? Did you see the async MVC controller action I showed in it? Did you see this action signature I showed in my answer: public async Task<IActionResult> Get() { ... }? Now in your question you are showing me a controller action signature like this public IActionResult Post(string url). Make it async like I showed in my answer. Make it public async Task<IActionResult> Post() { ... } so that you can await on the call of the async helper as shown in my answer. So just read my answer more carefully.
I've altered my code to show you my newly revised controller. I redid exactly what you outlined in your previous comment. Can you help me with an edit that will get rid of the errors I'm getting?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.