質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.35%

  • Visual Studio

    2544questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • Ajax

    1408questions

    Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

  • VB.NET

    1124questions

    Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

  • ASP.NET

    645questions

    ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

AjaxからWCFサービスを呼び出すと、401エラーが出ます。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,139

kurikurimaro

score 7

前提・問題点

Microsof Visual Studio Community 2017で開発しております。
Windows 2012 ServerにWCFサービスを配置し、IISで公開しています。
'Access-Control-Allow-Origin'ヘッダが無いため401(Unauthorized)が返るとのことで、Ajaxから呼び出すことができません。
(下記ソースコードの、WCFサービスにおけるGlobal.asaxがうまく読み込めていないためかと考えているのですが、)どのように修正したらよいでしょうか。また、気をつけるべき設定はあるでしょうか。
なお、URLがドメインを含んでいるのは、Cordovaでモバイルアプリ化を図ろうとしているためです。クロスドメインでも呼び出せるようにGlobal.asaxファイルを設けました。

エラーメッセージ

Chromeの開発ツールで確認すると、以下のようなメッセージが出ています。

1) Failed to load resource:the server responded with a status of 401(Unauthorized)
2) No 'Access-Control-Allow-Origin' header is present on the requested resource.

Chromeのエラーメッセージ

ソースコード

呼出側のAjaxの抜粋です。

            function SetselCate() {
                var iData = {};
                iData.Cate = 1;

                $.ajax({
                   //    url: '<%=ResolveUrl("~/SyouBu.svc/SetselCate")%>',  //これは用いない
                   url: 'http://*********.jp/SHOPserv/SyouBu.svc/SetselCate',
                    type: 'GET',
                    dataType: 'json',
                    contentType: "application/json; charset=utf-8",
                    data: iData,
                    success: function (response) {
                        var cateset = JSON.parse(response.d);
                        $.each(cateset, function () {
                            var setCateID = this.cateid;
                            var setCateNM = this.catenm;
                        });
                    },
                    error: function (xhr, status, err) {
                        console.log(xhr);
                        console.log(status);
                        console.log(err);
                        alert('通信失敗');

                    }
                });
            };    


WCFサービスです。ファイル名はWcfService1です。

Imports System.ServiceModel
Imports System.ServiceModel.Activation
Imports System.ServiceModel.Web
Imports System.Web.Script.Serialization

<ServiceContract(Namespace:="")>
<AspNetCompatibilityRequirements(RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)>
Public Class SyouBu
  <WebGet()>
    Public Function SetselCate(ByVal Cate As String) As String
       Dim syoubu As New List(Of Object)()
               syoubu.Add(New With {
                        Key .cateid = "カテゴリIDの返り値",
                        Key .catenm = "カテゴリ名の返り値"
                        })
        Return (New JavaScriptSerializer().Serialize(syoubu))
    End Function
End Class


WCFサービスのGlobal.asaxです。

Imports System.Web.SessionState

Public Class Global_asax
    Inherits System.Web.HttpApplication
    Protected Sub Application_BeginRequest(sender As Object, e As EventArgs)
        HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache)
        HttpContext.Current.Response.Cache.SetNoStore()
        EnableCrossDmainAjaxCall()
    End Sub

    Private Sub EnableCrossDmainAjaxCall()
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*")

        If HttpContext.Current.Request.HttpMethod = "OPTIONS" Then
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST")
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept")
            HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000")
            HttpContext.Current.Response.[End]()
        End If
    End Sub
End Class


Web.configの一部です。

 <system.serviceModel>
    <behaviors>
      <endpointBehaviors>   
        <behavior name="WcfService1.SyouBuAspNetAjaxBehavior">
          <enableWebScript />
        </behavior>
      </endpointBehaviors>

      <serviceBehaviors>
        <behavior name="ajaxServiceBehavior">
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

    <services>
         <service name="WcfService1.SyouBu">
              <endpoint address="" behaviorConfiguration="WcfService1.SyouBuAspNetAjaxBehavior" binding="webHttpBinding" contract="WcfService1.SyouBu" />
         </service>
    </services>

  </system.serviceModel>

試したこと

同一ドメインであれば問題なく期待通りの結果が得られていると思います。
開発環境(Windows10)のIISにWCFサービスを配置して試したところ、エラーは出ませんでした。
ローカルのIPアドレスが192.168.0.108 なので、AjaxのURLを以下のように書きました。
url: 'http://192.168.0.108/SHOPserv/SyouBu.svc/SetselCate',

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • SurferOnWww

    2016/12/27 12:32

    では、こちらで Q&A を続けるようお願いします。余計なお世話かもしれませんが、Stackoverflow の方はクローズした方がよさそうです。

    キャンセル

  • SurferOnWww

    2016/12/27 12:41

    「比べて不要なファイルが含まれているもの」とのことですが、自分が検証用に使った WCF サービスも Visual Studio のインターネット用テンプレートを使ったもので、フォーム認証に必要なコードが入っている Account フォルダその他 WCF サービスには不要なファイルが山ほど含まれています。なので、本来は不要なファイルがあっても問題はないはずです。ちなみに自分が検証用に使った WCF サービスは http://surferonwww.info/BlogEngine/post/2015/10/15/wcf-and-jquery-ajax.aspx の通りです。これを不要なファイルが山ほどある既存の ASP.NET Web Forms アプリに追加しています。

    キャンセル

  • kurikurimaro

    2017/02/20 20:34

    通常でも不要なファイルは多いのですね。引用のリンクのほうは、未だに問題の切り分けや原因はわからないのですが、とりあえず不要なのでクローズしておきました。

    キャンセル

回答 1

checkベストアンサー

+1

レスが遅くなりました。

ブラウザのバージョンが不明ですが最新版で Cross-Origin Resource Sharing をサポートしていて、問題は Cross-Origin Resource Sharing を利用してのクロスドメインの対応がうまくいってないだけと理解してレスします。

原因として考えられるのは Global.asax に設定した EnableCrossDmainAjaxCall() メソッドが期待通り動いてなくて Cross-Origin Resource Sharing に必要なヘッダが WCF サービスからの応答に含まれないことぐらいです。

質問者さんの EnableCrossDmainAjaxCall() メソッドのコードは多分以下の記事からコピペして "http://localhost:5187" を "*" に書き換えたのだと思います。コードは正しいと思いますが、それがうまく動いてないように思われます。

Consuming WCF REST Services Using jQuery AJAX Calls
https://www.codeproject.com/kb/ajax/jquerywcfrest.aspx

それがうまく動いていて必要なヘッダがやり取りされているかを、ブラウザと WCF サービスの間の要求・応答を Fiddler 等のキャプチャツールでキャプチャして調べてみるのが原因究明のために良いと思います。

質問者さんの jQuery.ajax のコードを見ると contentType: "application/json; charset=utf-8" が含まれているので、以下の記事で言う「シンプルなリクエスト」にはならず、まず「プリフライトリクエスト」が行われてクロスドメインアクセスが可能か確認してから WCF サービスのメソッドにアクセスしてデータを取得するという動きになるはずです。

HTTP アクセス制御 (CORS)
https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control

Fiddler で「プリフライトリクエスト」を見ると以下の画像ようになります。 

http://surferonwww.info/BlogEngine/image.axd?picture=2016%2f12%2fCORS.jpg

Request Headers で赤枠で囲った部分に注目してください、ブラウザが Cross-Origin Resource Sharing をサポートしていれば、クロスドメイン要求でかつ「シンプルなリクエスト」にならない場合、自動的にこのような「プリフライトリクエスト」のためのヘッダを送信してくれます。(使ったブラウザは Firefox 50.1.0 です。Chrome は若干違いますが肝心の部分は同じになるはずです)

サーバーが要求を受けると EnableCrossDmainAjaxCall() によって応答ヘッダが返されるはずです。質問者さんのコードが期待通り動いていれば以下のようになるはずです。

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Accept
Access-Control-Max-Age: 1728000

(注)Access-Control-Max-Age の設定があると「プリフライトリクエスト」の結果がキャッシュされるので、その時間が経過するまでの同一リソースに対するアクセスに関しては、ブラウザは「プリフライトリクエスト」を発行せずに実際のリクエストを直接送るので注意してください。検証中は削除しておいた方がいいかもしれません。

この応答を受けてからブラウザは WCF サービスのメソッドにアクセスしてデータを取得します。上の Fiddler の画像の左側のウィンドウでハイライトした次の行がそれです。

その応答にも EnableCrossDmainAjaxCall() によってヘッダに Access-Control-Allow-Origin: * が含まれるはずです。

#ところで、質問者さんの jQuery.ajax のコードですが、GET 要求であれば contentType: "application/json; charset=utf-8" の設定は不要なはずです。それを削除すれば「シンプルなリクエスト」になって、「プリフライトリクエスト」はスキップして応答が返ってくるはずです。そのとき応答ヘッダに Access-Control-Allow-Origin: * が含まれていれば、エラーにならず期待通りの結果になると思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/12/27 09:55

    丁寧な回答をありがとうございます。検証してみます。

    キャンセル

  • 2017/01/29 16:46

    >GET 要求であれば contentType: "application/json; charset=utf-8" の設定は不要
    このことにより、目的が達成できました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Visual Studio

    2544questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • Ajax

    1408questions

    Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

  • VB.NET

    1124questions

    Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

  • ASP.NET

    645questions

    ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。